callgraph_container.cpp

Go to the documentation of this file.
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 // we store {caller,callee} inside a single u64
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  * We need 2 comparators for callgraph to get the desired output:
00066  *
00067  *  caller_with_few_samples
00068  *  caller_with_many_samples
00069  * function_with_many_samples
00070  *  callee_with_many_samples
00071  *  callee_with_few_samples
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 // find the nearest bfd symbol for the given file offset and check it's
00089 // in range
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     // sorted by filepos so this will find the nearest
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     // if the offset is past the end of the symbol, we didn't find one
00110     u32 const end_offset = it->size() + it->filepos();
00111 
00112     if (offset >= end_offset) {
00113         // let's be verbose for now
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         // see profile_t::samples_range() for why we need this check
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             // Our odb_key_t contain (from_eip << 32 | to_eip),
00151             // the range of keys we selected above contains one
00152             // caller but different callees, and due to the
00153             // ordering callee offsets are not consecutive: so
00154             // we must sort them first.
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         // stop if we pass the end of the callee
00265         if (offset >= callee_end)
00266             break;
00267 
00268         count += it->second;
00269         ++it;
00270     }
00271 
00272     // If we haven't advanced at all, then we'll get
00273     // an infinite loop, so we must abort.
00274     if (it == start) {
00275         cerr << "failure to advance iterator\n";
00276         abort();
00277     }
00278 
00279     return count;
00280 }
00281 
00282 
00283 } // anonymous namespace
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     // If we have a callee, add it to the caller's list, then
00293     // add the caller to the callee's list.
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     // generate the synthetic self entry for the symbol
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     // FIXME: this relies on sort always being sample count
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         // threshold out the main symbol if needed
00354         if (op_ratio(sym.sample.counts[0], total[0]) < threshold)
00355             continue;
00356 
00357         // FIXME: slow?
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         // insert sym into cg_syms_objs
00383         // then store pointer to sym in cg_syms
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     // non callgraph samples container, we record sample at symbol level
00401     // not at vma level.
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         // populate_caller_image take care about empty sample filename
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         // We can't use start_offset support in profile_t, give
00492         // it a zero offset and we will fix that in add()
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     // We can't use kernel sample file w/o the binary else we will
00511     // use it with a zero offset, the code below will abort because
00512     // we will get incorrect callee sub-range and out of range
00513     // callee vma. FIXME
00514     if (header.is_kernel && !caller_bfd_ok)
00515         return;
00516 
00517     // We must handle start_offset, this offset can be different for the
00518     // caller and the callee: kernel sample traversing the syscall barrier.
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     // For each symbol in the caller bfd, process all arcs to
00546     // callee bfd symbols
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             // if we can't find the callee, skip an arc
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     // FIXME: costly: must we access directly recorder map ?
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 }

Generated on 8 Nov 2012 for Oprofile by  doxygen 1.6.1