00001
00012 #include <cstdlib>
00013
00014 #include <map>
00015 #include <set>
00016 #include <algorithm>
00017 #include <iterator>
00018 #include <string>
00019 #include <iostream>
00020 #include <numeric>
00021
00022 #include "callgraph_container.h"
00023 #include "cverb.h"
00024 #include "parse_filename.h"
00025 #include "profile_container.h"
00026 #include "arrange_profiles.h"
00027 #include "populate.h"
00028 #include "string_filter.h"
00029 #include "op_bfd.h"
00030 #include "op_sample_file.h"
00031 #include "locate_images.h"
00032
00033 using namespace std;
00034
00035 namespace {
00036
00037 bool operator==(cg_symbol const & lhs, cg_symbol const & rhs)
00038 {
00039 less_symbol cmp_symb;
00040 return !cmp_symb(lhs, rhs) && !cmp_symb(rhs, lhs);
00041 }
00042
00043
00044
00045 odb_key_t caller_to_key(u32 value)
00046 {
00047 return odb_key_t(value) << 32;
00048 }
00049
00050
00051 u32 key_to_callee(odb_key_t key)
00052 {
00053 return key & 0xffffffff;
00054 }
00055
00056
00057 bool compare_by_callee_vma(pair<odb_key_t, count_type> const & lhs,
00058 pair<odb_key_t, count_type> const & rhs)
00059 {
00060 return (key_to_callee(lhs.first)) < (key_to_callee(rhs.first));
00061 }
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 bool
00075 compare_arc_count(symbol_entry const & lhs, symbol_entry const & rhs)
00076 {
00077 return lhs.sample.counts[0] < rhs.sample.counts[0];
00078 }
00079
00080
00081 bool
00082 compare_arc_count_reverse(symbol_entry const & lhs, symbol_entry const & rhs)
00083 {
00084 return rhs.sample.counts[0] < lhs.sample.counts[0];
00085 }
00086
00087
00088
00089
00090 op_bfd_symbol const *
00091 get_symbol_by_filepos(op_bfd const & bfd, u32 bfd_offset,
00092 vma_t offset, symbol_index_t & i)
00093 {
00094 offset += bfd_offset;
00095 op_bfd_symbol tmpsym(offset, 0, string());
00096
00097
00098 vector<op_bfd_symbol>::const_iterator it =
00099 upper_bound(bfd.syms.begin(), bfd.syms.end(), tmpsym);
00100
00101 if (it != bfd.syms.begin())
00102 --it;
00103
00104 if (it == bfd.syms.end()) {
00105 cerr << "get_symbol_by_filepos: no symbols at all?" << endl;
00106 abort();
00107 }
00108
00109
00110 u32 const end_offset = it->size() + it->filepos();
00111
00112 if (offset >= end_offset) {
00113
00114 cerr << "warning: dropping hyperspace sample at offset "
00115 << hex << offset << " >= " << end_offset
00116 << " for binary " << bfd.get_filename() << dec << endl;
00117 return NULL;
00118 }
00119
00120 i = distance(bfd.syms.begin(), it);
00121 return &(*it);
00122 }
00123
00124
00126 class call_data {
00127 public:
00128 call_data(profile_container const & p, profile_t const & pr,
00129 op_bfd const & bfd, u32 boff, image_name_id iid,
00130 image_name_id aid, bool debug_info)
00131 : pc(p), profile(pr), b(bfd), boffset(boff), image(iid),
00132 app(aid), debug(debug_info) {}
00133
00135 void caller_sym(symbol_index_t i) {
00136 sym = symbol_entry();
00137
00138 unsigned long long start;
00139 unsigned long long end;
00140 b.get_symbol_range(i, start, end);
00141
00142 samples.clear();
00143
00144
00145 if (start > boffset) {
00146 profile_t::iterator_pair p_it = profile.samples_range(
00147 caller_to_key(start - boffset),
00148 caller_to_key(end - boffset));
00149
00150
00151
00152
00153
00154
00155
00156 for (; p_it.first != p_it.second; ++p_it.first) {
00157 samples.push_back(make_pair(p_it.first.vma(),
00158 p_it.first.count()));
00159 }
00160
00161 sort(samples.begin(), samples.end(),
00162 compare_by_callee_vma);
00163 }
00164
00165 sym.size = end - start;
00166 sym.name = symbol_names.create(b.syms[i].name());
00167 sym.sample.vma = b.syms[i].vma();
00168
00169 finish_sym(i, start);
00170
00171 if (cverb << vdebug) {
00172 cverb << vdebug << hex << "Caller sym: "
00173 << b.syms[i].name() << " filepos " << start
00174 << "-" << end << dec << endl;
00175 }
00176 }
00177
00179 bool callee_sym(u32 off) {
00180 sym = symbol_entry();
00181
00182 symbol_index_t i = 0;
00183 op_bfd_symbol const * bfdsym =
00184 get_symbol_by_filepos(b, boffset, off, i);
00185
00186 if (!bfdsym)
00187 return false;
00188
00189 callee_end = bfdsym->size() + bfdsym->filepos() - boffset;
00190
00191 sym.size = bfdsym->size();
00192 sym.name = symbol_names.create(bfdsym->name());
00193 sym.sample.vma = bfdsym->vma();
00194
00195 finish_sym(i, bfdsym->filepos());
00196
00197 if (cverb << vdebug) {
00198 cverb << vdebug << hex << "Callee sym: "
00199 << bfdsym->name() << " filepos "
00200 << bfdsym->filepos() << "-"
00201 << (bfdsym->filepos() + bfdsym->size())
00202 << dec << endl;
00203 }
00204 return true;
00205 }
00206
00207 void verbose_bfd(string const & prefix) const {
00208 cverb << vdebug << prefix << " " << b.get_filename()
00209 << " offset " << boffset << " app "
00210 << image_names.name(app) << endl;
00211 }
00212
00213 typedef vector<pair<odb_key_t, count_type> > samples_t;
00214
00215 typedef samples_t::const_iterator const_iterator;
00216
00217 samples_t samples;
00218 symbol_entry sym;
00219 u32 callee_end;
00220
00221 private:
00223 void finish_sym(symbol_index_t i, unsigned long start) {
00224 sym.image_name = image;
00225 sym.app_name = app;
00226 symbol_entry const * self = pc.find(sym);
00227 if (self)
00228 sym.sample.counts = self->sample.counts;
00229
00230 if (debug) {
00231 string filename;
00232 file_location & loc = sym.sample.file_loc;
00233 if (b.get_linenr(i, start, filename, loc.linenr))
00234 loc.filename = debug_names.create(filename);
00235 }
00236 }
00237
00238 profile_container const & pc;
00239 profile_t const & profile;
00240 op_bfd const & b;
00241 u32 boffset;
00242 image_name_id image;
00243 image_name_id app;
00244 bool debug;
00245 };
00246
00247
00249 count_type
00250 accumulate_callee(call_data::const_iterator & it, call_data::const_iterator end,
00251 u32 callee_end)
00252 {
00253 count_type count = 0;
00254 call_data::const_iterator const start = it;
00255
00256 while (it != end) {
00257 u32 offset = key_to_callee(it->first);
00258
00259 if (cverb << (vdebug & vlevel1)) {
00260 cverb << (vdebug & vlevel1) << hex << "offset: "
00261 << offset << dec << endl;
00262 }
00263
00264
00265 if (offset >= callee_end)
00266 break;
00267
00268 count += it->second;
00269 ++it;
00270 }
00271
00272
00273
00274 if (it == start) {
00275 cerr << "failure to advance iterator\n";
00276 abort();
00277 }
00278
00279 return count;
00280 }
00281
00282
00283 }
00284
00285
00286 void arc_recorder::
00287 add(symbol_entry const & caller, symbol_entry const * callee,
00288 count_array_t const & arc_count)
00289 {
00290 cg_data & data = sym_map[caller];
00291
00292
00293
00294 if (callee) {
00295 data.callees[*callee] += arc_count;
00296
00297 cg_data & callee_data = sym_map[*callee];
00298
00299 callee_data.callers[caller] += arc_count;
00300 }
00301 }
00302
00303
00304 void arc_recorder::process_children(cg_symbol & sym, double threshold)
00305 {
00306
00307 symbol_entry self = sym;
00308
00309 self.name = symbol_names.create(symbol_names.demangle(self.name)
00310 + " [self]");
00311
00312 sym.total_callee_count += self.sample.counts;
00313 sym.callees.push_back(self);
00314
00315 sort(sym.callers.begin(), sym.callers.end(), compare_arc_count);
00316 sort(sym.callees.begin(), sym.callees.end(), compare_arc_count_reverse);
00317
00318
00319
00320 cg_symbol::children::iterator cit = sym.callers.begin();
00321 cg_symbol::children::iterator cend = sym.callers.end();
00322
00323 while (cit != cend && op_ratio(cit->sample.counts[0],
00324 sym.total_caller_count[0]) < threshold)
00325 ++cit;
00326
00327 if (cit != cend)
00328 sym.callers.erase(sym.callers.begin(), cit);
00329
00330 cit = sym.callees.begin();
00331 cend = sym.callees.end();
00332
00333 while (cit != cend && op_ratio(cit->sample.counts[0],
00334 sym.total_callee_count[0]) >= threshold)
00335 ++cit;
00336
00337 if (cit != cend)
00338 sym.callees.erase(cit, sym.callees.end());
00339 }
00340
00341
00342 void arc_recorder::
00343 process(count_array_t total, double threshold,
00344 string_filter const & sym_filter)
00345 {
00346 map_t::const_iterator it;
00347 map_t::const_iterator end = sym_map.end();
00348
00349 for (it = sym_map.begin(); it != end; ++it) {
00350 cg_symbol sym((*it).first);
00351 cg_data const & data = (*it).second;
00352
00353
00354 if (op_ratio(sym.sample.counts[0], total[0]) < threshold)
00355 continue;
00356
00357
00358 if (!sym_filter.match(symbol_names.demangle(sym.name)))
00359 continue;
00360
00361 cg_data::children::const_iterator cit;
00362 cg_data::children::const_iterator cend = data.callers.end();
00363
00364 for (cit = data.callers.begin(); cit != cend; ++cit) {
00365 symbol_entry csym = cit->first;
00366 csym.sample.counts = cit->second;
00367 sym.callers.push_back(csym);
00368 sym.total_caller_count += cit->second;
00369 }
00370
00371 cend = data.callees.end();
00372
00373 for (cit = data.callees.begin(); cit != cend; ++cit) {
00374 symbol_entry csym = cit->first;
00375 csym.sample.counts = cit->second;
00376 sym.callees.push_back(csym);
00377 sym.total_callee_count += cit->second;
00378 }
00379
00380 process_children(sym, threshold);
00381
00382
00383
00384 cg_syms.push_back(&(*cg_syms_objs.insert(cg_syms_objs.end(), sym)));
00385 }
00386 }
00387
00388
00389 symbol_collection const & arc_recorder::get_symbols() const
00390 {
00391 return cg_syms;
00392 }
00393
00394
00395 void callgraph_container::populate(list<inverted_profile> const & iprofiles,
00396 extra_images const & extra, bool debug_info, double threshold,
00397 bool merge_lib, string_filter const & sym_filter)
00398 {
00399 this->extra_found_images = extra;
00400
00401
00402 profile_container pc(debug_info, false, extra_found_images);
00403
00404 list<inverted_profile>::const_iterator it;
00405 list<inverted_profile>::const_iterator const end = iprofiles.end();
00406 for (it = iprofiles.begin(); it != end; ++it) {
00407
00408 populate_for_image(pc, *it, sym_filter, 0);
00409 }
00410
00411 add_symbols(pc);
00412
00413 total_count = pc.samples_count();
00414
00415 for (it = iprofiles.begin(); it != end; ++it) {
00416 for (size_t i = 0; i < it->groups.size(); ++i) {
00417 populate(it->groups[i], it->image,
00418 i, pc, debug_info, merge_lib);
00419 }
00420 }
00421
00422 recorder.process(total_count, threshold / 100.0, sym_filter);
00423 }
00424
00425
00426 void callgraph_container::populate(list<image_set> const & lset,
00427 string const & app_image, size_t pclass,
00428 profile_container const & pc, bool debug_info, bool merge_lib)
00429 {
00430 list<image_set>::const_iterator lit;
00431 list<image_set>::const_iterator const lend = lset.end();
00432 for (lit = lset.begin(); lit != lend; ++lit) {
00433 list<profile_sample_files>::const_iterator pit;
00434 list<profile_sample_files>::const_iterator pend
00435 = lit->files.end();
00436 for (pit = lit->files.begin(); pit != pend; ++pit) {
00437 populate(pit->cg_files, app_image,
00438 pclass, pc, debug_info, merge_lib);
00439 }
00440 }
00441 }
00442
00443
00444 void callgraph_container::populate(list<string> const & cg_files,
00445 string const & app_image, size_t pclass,
00446 profile_container const & pc, bool debug_info, bool merge_lib)
00447 {
00448 list<string>::const_iterator it;
00449 list<string>::const_iterator const end = cg_files.end();
00450 for (it = cg_files.begin(); it != end; ++it) {
00451 cverb << vdebug << "samples file : " << *it << endl;
00452
00453 parsed_filename caller_file =
00454 parse_filename(*it, extra_found_images);
00455 string const app_name = caller_file.image;
00456
00457 image_error error;
00458 extra_found_images.find_image_path(caller_file.lib_image,
00459 error, false);
00460
00461 if (error != image_ok)
00462 report_image_error(caller_file.lib_image,
00463 error, false, extra_found_images);
00464
00465 bool caller_bfd_ok = true;
00466 op_bfd caller_bfd(caller_file.lib_image,
00467 string_filter(), extra_found_images, caller_bfd_ok);
00468 if (!caller_bfd_ok)
00469 report_image_error(caller_file.lib_image,
00470 image_format_failure, false,
00471 extra_found_images);
00472
00473 parsed_filename callee_file =
00474 parse_filename(*it, extra_found_images);
00475
00476 extra_found_images.find_image_path(callee_file.cg_image,
00477 error, false);
00478 if (error != image_ok)
00479 report_image_error(callee_file.cg_image,
00480 error, false, extra_found_images);
00481
00482 bool callee_bfd_ok = true;
00483 op_bfd callee_bfd(callee_file.cg_image,
00484 string_filter(), extra_found_images, callee_bfd_ok);
00485 if (!callee_bfd_ok)
00486 report_image_error(callee_file.cg_image,
00487 image_format_failure, false,
00488 extra_found_images);
00489
00490 profile_t profile;
00491
00492
00493 profile.add_sample_file(*it);
00494 add(profile, caller_bfd, caller_bfd_ok, callee_bfd,
00495 merge_lib ? app_image : app_name, pc,
00496 debug_info, pclass);
00497 }
00498 }
00499
00500
00501 void callgraph_container::
00502 add(profile_t const & profile, op_bfd const & caller_bfd, bool caller_bfd_ok,
00503 op_bfd const & callee_bfd, string const & app_name,
00504 profile_container const & pc, bool debug_info, size_t pclass)
00505 {
00506 string const image_name = caller_bfd.get_filename();
00507
00508 opd_header const & header = profile.get_header();
00509
00510
00511
00512
00513
00514 if (header.is_kernel && !caller_bfd_ok)
00515 return;
00516
00517
00518
00519 u32 caller_offset;
00520 if (header.is_kernel)
00521 caller_offset = caller_bfd.get_start_offset(0);
00522 else
00523 caller_offset = header.anon_start;
00524
00525 u32 callee_offset;
00526 if (header.cg_to_is_kernel)
00527 callee_offset = callee_bfd.get_start_offset(0);
00528 else
00529 callee_offset = header.cg_to_anon_start;
00530
00531 image_name_id image_id = image_names.create(image_name);
00532 image_name_id callee_image_id = image_names.create(callee_bfd.get_filename());
00533 image_name_id app_id = image_names.create(app_name);
00534
00535 call_data caller(pc, profile, caller_bfd, caller_offset, image_id,
00536 app_id, debug_info);
00537 call_data callee(pc, profile, callee_bfd, callee_offset,
00538 callee_image_id, app_id, debug_info);
00539
00540 if (cverb << vdebug) {
00541 caller.verbose_bfd("Caller:");
00542 callee.verbose_bfd("Callee:");
00543 }
00544
00545
00546
00547
00548 for (symbol_index_t i = 0; i < caller_bfd.syms.size(); ++i) {
00549
00550 caller.caller_sym(i);
00551
00552 call_data::const_iterator dit = caller.samples.begin();
00553 call_data::const_iterator dend = caller.samples.end();
00554 while (dit != dend) {
00555
00556 if (!callee.callee_sym(key_to_callee(dit->first))) {
00557 ++dit;
00558 continue;
00559 }
00560
00561 count_array_t arc_count;
00562 arc_count[pclass] =
00563 accumulate_callee(dit, dend, callee.callee_end);
00564
00565 recorder.add(caller.sym, &callee.sym, arc_count);
00566 }
00567 }
00568 }
00569
00570
00571 void callgraph_container::add_symbols(profile_container const & pc)
00572 {
00573 symbol_container::symbols_t::iterator it;
00574 symbol_container::symbols_t::iterator const end = pc.end_symbol();
00575
00576 for (it = pc.begin_symbol(); it != end; ++it)
00577 recorder.add(*it, 0, count_array_t());
00578 }
00579
00580
00581 column_flags callgraph_container::output_hint() const
00582 {
00583 column_flags output_hints = cf_none;
00584
00585
00586 symbol_collection syms = recorder.get_symbols();
00587
00588 symbol_collection::iterator it;
00589 symbol_collection::iterator const end = syms.end();
00590 for (it = syms.begin(); it != end; ++it)
00591 output_hints = (*it)->output_hint(output_hints);
00592
00593 return output_hints;
00594 }
00595
00596
00597 count_array_t callgraph_container::samples_count() const
00598 {
00599 return total_count;
00600 }
00601
00602
00603 symbol_collection const & callgraph_container::get_symbols() const
00604 {
00605 return recorder.get_symbols();
00606 }