1 /** 2 * @file callgraph_container.cpp 3 * Container associating symbols and caller/caller symbols 4 * 5 * @remark Copyright 2004 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author Philippe Elie 9 * @author John Levon 10 */ 11 12 #include <cstdlib> 13 14 #include <map> 15 #include <set> 16 #include <algorithm> 17 #include <iterator> 18 #include <string> 19 #include <iostream> 20 #include <numeric> 21 22 #include "callgraph_container.h" 23 #include "cverb.h" 24 #include "parse_filename.h" 25 #include "profile_container.h" 26 #include "arrange_profiles.h" 27 #include "populate.h" 28 #include "string_filter.h" 29 #include "op_bfd.h" 30 #include "op_sample_file.h" 31 #include "locate_images.h" 32 33 using namespace std; 34 35 namespace { 36 37 bool operator==(cg_symbol const & lhs, cg_symbol const & rhs) 38 { 39 less_symbol cmp_symb; 40 return !cmp_symb(lhs, rhs) && !cmp_symb(rhs, lhs); 41 } 42 43 44 // we store {caller,callee} inside a single u64 45 odb_key_t caller_to_key(u32 value) 46 { 47 return odb_key_t(value) << 32; 48 } 49 50 51 u32 key_to_callee(odb_key_t key) 52 { 53 return key & 0xffffffff; 54 } 55 56 57 bool compare_by_callee_vma(pair<odb_key_t, count_type> const & lhs, 58 pair<odb_key_t, count_type> const & rhs) 59 { 60 return (key_to_callee(lhs.first)) < (key_to_callee(rhs.first)); 61 } 62 63 64 /* 65 * We need 2 comparators for callgraph to get the desired output: 66 * 67 * caller_with_few_samples 68 * caller_with_many_samples 69 * function_with_many_samples 70 * callee_with_many_samples 71 * callee_with_few_samples 72 */ 73 74 bool 75 compare_arc_count(symbol_entry const & lhs, symbol_entry const & rhs) 76 { 77 return lhs.sample.counts[0] < rhs.sample.counts[0]; 78 } 79 80 81 bool 82 compare_arc_count_reverse(symbol_entry const & lhs, symbol_entry const & rhs) 83 { 84 return rhs.sample.counts[0] < lhs.sample.counts[0]; 85 } 86 87 88 // find the nearest bfd symbol for the given file offset and check it's 89 // in range 90 op_bfd_symbol const * 91 get_symbol_by_filepos(op_bfd const & bfd, u32 bfd_offset, 92 vma_t offset, symbol_index_t & i) 93 { 94 offset += bfd_offset; 95 op_bfd_symbol tmpsym(offset, 0, string()); 96 97 // sorted by filepos so this will find the nearest 98 vector<op_bfd_symbol>::const_iterator it = 99 upper_bound(bfd.syms.begin(), bfd.syms.end(), tmpsym); 100 101 if (it != bfd.syms.begin()) 102 --it; 103 104 if (it == bfd.syms.end()) { 105 cerr << "get_symbol_by_filepos: no symbols at all?" << endl; 106 abort(); 107 } 108 109 // if the offset is past the end of the symbol, we didn't find one 110 u32 const end_offset = it->size() + it->filepos(); 111 112 if (offset >= end_offset) { 113 // let's be verbose for now 114 cerr << "warning: dropping hyperspace sample at offset " 115 << hex << offset << " >= " << end_offset 116 << " for binary " << bfd.get_filename() << dec << endl; 117 return NULL; 118 } 119 120 i = distance(bfd.syms.begin(), it); 121 return &(*it); 122 } 123 124 125 /// temporary caller and callee data held during processing 126 class call_data { 127 public: 128 call_data(profile_container const & p, profile_t const & pr, 129 op_bfd const & bfd, u32 boff, image_name_id iid, 130 image_name_id aid, bool debug_info) 131 : pc(p), profile(pr), b(bfd), boffset(boff), image(iid), 132 app(aid), debug(debug_info) {} 133 134 /// point to a caller symbol 135 void caller_sym(symbol_index_t i) { 136 sym = symbol_entry(); 137 138 unsigned long long start; 139 unsigned long long end; 140 b.get_symbol_range(i, start, end); 141 142 samples.clear(); 143 144 // see profile_t::samples_range() for why we need this check 145 if (start > boffset) { 146 profile_t::iterator_pair p_it = profile.samples_range( 147 caller_to_key(start - boffset), 148 caller_to_key(end - boffset)); 149 150 // Our odb_key_t contain (from_eip << 32 | to_eip), 151 // the range of keys we selected above contains one 152 // caller but different callees, and due to the 153 // ordering callee offsets are not consecutive: so 154 // we must sort them first. 155 156 for (; p_it.first != p_it.second; ++p_it.first) { 157 samples.push_back(make_pair(p_it.first.vma(), 158 p_it.first.count())); 159 } 160 161 sort(samples.begin(), samples.end(), 162 compare_by_callee_vma); 163 } 164 165 sym.size = end - start; 166 sym.name = symbol_names.create(b.syms[i].name()); 167 sym.sample.vma = b.syms[i].vma(); 168 169 finish_sym(i, start); 170 171 if (cverb << vdebug) { 172 cverb << vdebug << hex << "Caller sym: " 173 << b.syms[i].name() << " filepos " << start 174 << "-" << end << dec << endl; 175 } 176 } 177 178 /// point to a callee symbol 179 bool callee_sym(u32 off) { 180 sym = symbol_entry(); 181 182 symbol_index_t i = 0; 183 op_bfd_symbol const * bfdsym = 184 get_symbol_by_filepos(b, boffset, off, i); 185 186 if (!bfdsym) 187 return false; 188 189 callee_end = bfdsym->size() + bfdsym->filepos() - boffset; 190 191 sym.size = bfdsym->size(); 192 sym.name = symbol_names.create(bfdsym->name()); 193 sym.sample.vma = bfdsym->vma(); 194 195 finish_sym(i, bfdsym->filepos()); 196 197 if (cverb << vdebug) { 198 cverb << vdebug << hex << "Callee sym: " 199 << bfdsym->name() << " filepos " 200 << bfdsym->filepos() << "-" 201 << (bfdsym->filepos() + bfdsym->size()) 202 << dec << endl; 203 } 204 return true; 205 } 206 207 void verbose_bfd(string const & prefix) const { 208 cverb << vdebug << prefix << " " << b.get_filename() 209 << " offset " << boffset << " app " 210 << image_names.name(app) << endl; 211 } 212 213 typedef vector<pair<odb_key_t, count_type> > samples_t; 214 215 typedef samples_t::const_iterator const_iterator; 216 217 samples_t samples; 218 symbol_entry sym; 219 u32 callee_end; 220 221 private: 222 /// fill in the rest of the sym 223 void finish_sym(symbol_index_t i, unsigned long start) { 224 sym.image_name = image; 225 sym.app_name = app; 226 symbol_entry const * self = pc.find(sym); 227 if (self) 228 sym.sample.counts = self->sample.counts; 229 230 if (debug) { 231 string filename; 232 file_location & loc = sym.sample.file_loc; 233 if (b.get_linenr(i, start, filename, loc.linenr)) 234 loc.filename = debug_names.create(filename); 235 } 236 } 237 238 profile_container const & pc; 239 profile_t const & profile; 240 op_bfd const & b; 241 u32 boffset; 242 image_name_id image; 243 image_name_id app; 244 bool debug; 245 }; 246 247 248 /// accumulate all samples for a given caller/callee pair 249 count_type 250 accumulate_callee(call_data::const_iterator & it, call_data::const_iterator end, 251 u32 callee_end) 252 { 253 count_type count = 0; 254 call_data::const_iterator const start = it; 255 256 while (it != end) { 257 u32 offset = key_to_callee(it->first); 258 259 if (cverb << (vdebug & vlevel1)) { 260 cverb << (vdebug & vlevel1) << hex << "offset: " 261 << offset << dec << endl; 262 } 263 264 // stop if we pass the end of the callee 265 if (offset >= callee_end) 266 break; 267 268 count += it->second; 269 ++it; 270 } 271 272 // If we haven't advanced at all, then we'll get 273 // an infinite loop, so we must abort. 274 if (it == start) { 275 cerr << "failure to advance iterator\n"; 276 abort(); 277 } 278 279 return count; 280 } 281 282 283 } // anonymous namespace 284 285 286 void arc_recorder:: 287 add(symbol_entry const & caller, symbol_entry const * callee, 288 count_array_t const & arc_count) 289 { 290 cg_data & data = sym_map[caller]; 291 292 // If we have a callee, add it to the caller's list, then 293 // add the caller to the callee's list. 294 if (callee) { 295 data.callees[*callee] += arc_count; 296 297 cg_data & callee_data = sym_map[*callee]; 298 299 callee_data.callers[caller] += arc_count; 300 } 301 } 302 303 304 void arc_recorder::process_children(cg_symbol & sym, double threshold) 305 { 306 // generate the synthetic self entry for the symbol 307 symbol_entry self = sym; 308 309 self.name = symbol_names.create(symbol_names.demangle(self.name) 310 + " [self]"); 311 312 sym.total_callee_count += self.sample.counts; 313 sym.callees.push_back(self); 314 315 sort(sym.callers.begin(), sym.callers.end(), compare_arc_count); 316 sort(sym.callees.begin(), sym.callees.end(), compare_arc_count_reverse); 317 318 // FIXME: this relies on sort always being sample count 319 320 cg_symbol::children::iterator cit = sym.callers.begin(); 321 cg_symbol::children::iterator cend = sym.callers.end(); 322 323 while (cit != cend && op_ratio(cit->sample.counts[0], 324 sym.total_caller_count[0]) < threshold) 325 ++cit; 326 327 if (cit != cend) 328 sym.callers.erase(sym.callers.begin(), cit); 329 330 cit = sym.callees.begin(); 331 cend = sym.callees.end(); 332 333 while (cit != cend && op_ratio(cit->sample.counts[0], 334 sym.total_callee_count[0]) >= threshold) 335 ++cit; 336 337 if (cit != cend) 338 sym.callees.erase(cit, sym.callees.end()); 339 } 340 341 342 void arc_recorder:: 343 process(count_array_t total, double threshold, 344 string_filter const & sym_filter) 345 { 346 map_t::const_iterator it; 347 map_t::const_iterator end = sym_map.end(); 348 349 for (it = sym_map.begin(); it != end; ++it) { 350 cg_symbol sym((*it).first); 351 cg_data const & data = (*it).second; 352 353 // threshold out the main symbol if needed 354 if (op_ratio(sym.sample.counts[0], total[0]) < threshold) 355 continue; 356 357 // FIXME: slow? 358 if (!sym_filter.match(symbol_names.demangle(sym.name))) 359 continue; 360 361 cg_data::children::const_iterator cit; 362 cg_data::children::const_iterator cend = data.callers.end(); 363 364 for (cit = data.callers.begin(); cit != cend; ++cit) { 365 symbol_entry csym = cit->first; 366 csym.sample.counts = cit->second; 367 sym.callers.push_back(csym); 368 sym.total_caller_count += cit->second; 369 } 370 371 cend = data.callees.end(); 372 373 for (cit = data.callees.begin(); cit != cend; ++cit) { 374 symbol_entry csym = cit->first; 375 csym.sample.counts = cit->second; 376 sym.callees.push_back(csym); 377 sym.total_callee_count += cit->second; 378 } 379 380 process_children(sym, threshold); 381 382 // insert sym into cg_syms_objs 383 // then store pointer to sym in cg_syms 384 cg_syms.push_back(&(*cg_syms_objs.insert(cg_syms_objs.end(), sym))); 385 } 386 } 387 388 389 symbol_collection const & arc_recorder::get_symbols() const 390 { 391 return cg_syms; 392 } 393 394 395 void callgraph_container::populate(list<inverted_profile> const & iprofiles, 396 extra_images const & extra, bool debug_info, double threshold, 397 bool merge_lib, string_filter const & sym_filter) 398 { 399 this->extra_found_images = extra; 400 // non callgraph samples container, we record sample at symbol level 401 // not at vma level. 402 profile_container pc(debug_info, false, extra_found_images); 403 404 list<inverted_profile>::const_iterator it; 405 list<inverted_profile>::const_iterator const end = iprofiles.end(); 406 for (it = iprofiles.begin(); it != end; ++it) { 407 // populate_caller_image take care about empty sample filename 408 populate_for_image(pc, *it, sym_filter, 0); 409 } 410 411 add_symbols(pc); 412 413 total_count = pc.samples_count(); 414 415 for (it = iprofiles.begin(); it != end; ++it) { 416 for (size_t i = 0; i < it->groups.size(); ++i) { 417 populate(it->groups[i], it->image, 418 i, pc, debug_info, merge_lib); 419 } 420 } 421 422 recorder.process(total_count, threshold / 100.0, sym_filter); 423 } 424 425 426 void callgraph_container::populate(list<image_set> const & lset, 427 string const & app_image, size_t pclass, 428 profile_container const & pc, bool debug_info, bool merge_lib) 429 { 430 list<image_set>::const_iterator lit; 431 list<image_set>::const_iterator const lend = lset.end(); 432 for (lit = lset.begin(); lit != lend; ++lit) { 433 list<profile_sample_files>::const_iterator pit; 434 list<profile_sample_files>::const_iterator pend 435 = lit->files.end(); 436 for (pit = lit->files.begin(); pit != pend; ++pit) { 437 populate(pit->cg_files, app_image, 438 pclass, pc, debug_info, merge_lib); 439 } 440 } 441 } 442 443 444 void callgraph_container::populate(list<string> const & cg_files, 445 string const & app_image, size_t pclass, 446 profile_container const & pc, bool debug_info, bool merge_lib) 447 { 448 list<string>::const_iterator it; 449 list<string>::const_iterator const end = cg_files.end(); 450 for (it = cg_files.begin(); it != end; ++it) { 451 cverb << vdebug << "samples file : " << *it << endl; 452 453 parsed_filename caller_file = 454 parse_filename(*it, extra_found_images); 455 string const app_name = caller_file.image; 456 457 image_error error; 458 extra_found_images.find_image_path(caller_file.lib_image, 459 error, false); 460 461 if (error != image_ok) 462 report_image_error(caller_file.lib_image, 463 error, false, extra_found_images); 464 465 bool caller_bfd_ok = true; 466 op_bfd caller_bfd(caller_file.lib_image, 467 string_filter(), extra_found_images, caller_bfd_ok); 468 if (!caller_bfd_ok) 469 report_image_error(caller_file.lib_image, 470 image_format_failure, false, 471 extra_found_images); 472 473 parsed_filename callee_file = 474 parse_filename(*it, extra_found_images); 475 476 extra_found_images.find_image_path(callee_file.cg_image, 477 error, false); 478 if (error != image_ok) 479 report_image_error(callee_file.cg_image, 480 error, false, extra_found_images); 481 482 bool callee_bfd_ok = true; 483 op_bfd callee_bfd(callee_file.cg_image, 484 string_filter(), extra_found_images, callee_bfd_ok); 485 if (!callee_bfd_ok) 486 report_image_error(callee_file.cg_image, 487 image_format_failure, false, 488 extra_found_images); 489 490 profile_t profile; 491 // We can't use start_offset support in profile_t, give 492 // it a zero offset and we will fix that in add() 493 profile.add_sample_file(*it); 494 add(profile, caller_bfd, caller_bfd_ok, callee_bfd, 495 merge_lib ? app_image : app_name, pc, 496 debug_info, pclass); 497 } 498 } 499 500 501 void callgraph_container:: 502 add(profile_t const & profile, op_bfd const & caller_bfd, bool caller_bfd_ok, 503 op_bfd const & callee_bfd, string const & app_name, 504 profile_container const & pc, bool debug_info, size_t pclass) 505 { 506 string const image_name = caller_bfd.get_filename(); 507 508 opd_header const & header = profile.get_header(); 509 510 // We can't use kernel sample file w/o the binary else we will 511 // use it with a zero offset, the code below will abort because 512 // we will get incorrect callee sub-range and out of range 513 // callee vma. FIXME 514 if (header.is_kernel && !caller_bfd_ok) 515 return; 516 517 // We must handle start_offset, this offset can be different for the 518 // caller and the callee: kernel sample traversing the syscall barrier. 519 u32 caller_offset; 520 if (header.is_kernel) 521 caller_offset = caller_bfd.get_start_offset(0); 522 else 523 caller_offset = header.anon_start; 524 525 u32 callee_offset; 526 if (header.cg_to_is_kernel) 527 callee_offset = callee_bfd.get_start_offset(0); 528 else 529 callee_offset = header.cg_to_anon_start; 530 531 image_name_id image_id = image_names.create(image_name); 532 image_name_id callee_image_id = image_names.create(callee_bfd.get_filename()); 533 image_name_id app_id = image_names.create(app_name); 534 535 call_data caller(pc, profile, caller_bfd, caller_offset, image_id, 536 app_id, debug_info); 537 call_data callee(pc, profile, callee_bfd, callee_offset, 538 callee_image_id, app_id, debug_info); 539 540 if (cverb << vdebug) { 541 caller.verbose_bfd("Caller:"); 542 callee.verbose_bfd("Callee:"); 543 } 544 545 // For each symbol in the caller bfd, process all arcs to 546 // callee bfd symbols 547 548 for (symbol_index_t i = 0; i < caller_bfd.syms.size(); ++i) { 549 550 caller.caller_sym(i); 551 552 call_data::const_iterator dit = caller.samples.begin(); 553 call_data::const_iterator dend = caller.samples.end(); 554 while (dit != dend) { 555 // if we can't find the callee, skip an arc 556 if (!callee.callee_sym(key_to_callee(dit->first))) { 557 ++dit; 558 continue; 559 } 560 561 count_array_t arc_count; 562 arc_count[pclass] = 563 accumulate_callee(dit, dend, callee.callee_end); 564 565 recorder.add(caller.sym, &callee.sym, arc_count); 566 } 567 } 568 } 569 570 571 void callgraph_container::add_symbols(profile_container const & pc) 572 { 573 symbol_container::symbols_t::iterator it; 574 symbol_container::symbols_t::iterator const end = pc.end_symbol(); 575 576 for (it = pc.begin_symbol(); it != end; ++it) 577 recorder.add(*it, 0, count_array_t()); 578 } 579 580 581 column_flags callgraph_container::output_hint() const 582 { 583 column_flags output_hints = cf_none; 584 585 // FIXME: costly: must we access directly recorder map ? 586 symbol_collection syms = recorder.get_symbols(); 587 588 symbol_collection::iterator it; 589 symbol_collection::iterator const end = syms.end(); 590 for (it = syms.begin(); it != end; ++it) 591 output_hints = (*it)->output_hint(output_hints); 592 593 return output_hints; 594 } 595 596 597 count_array_t callgraph_container::samples_count() const 598 { 599 return total_count; 600 } 601 602 603 symbol_collection const & callgraph_container::get_symbols() const 604 { 605 return recorder.get_symbols(); 606 } 607