| /** |
| * @file profile.cpp |
| * Encapsulation for samples files over all profile classes |
| * belonging to the same binary image |
| * |
| * @remark Copyright 2002 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author Philippe Elie |
| * @author John Levon |
| */ |
| |
| #include <unistd.h> |
| #include <cstring> |
| |
| #include <iostream> |
| #include <string> |
| #include <sstream> |
| #include <cstring> |
| |
| #include <cerrno> |
| |
| #include "op_exception.h" |
| #include "op_header.h" |
| #include "op_config.h" |
| #include "op_sample_file.h" |
| #include "profile.h" |
| #include "op_bfd.h" |
| #include "cverb.h" |
| #include "populate_for_spu.h" |
| |
| using namespace std; |
| |
| profile_t::profile_t() |
| : start_offset(0) |
| { |
| } |
| |
| |
| // static member |
| count_type profile_t::sample_count(string const & filename) |
| { |
| odb_t samples_db; |
| |
| open_sample_file(filename, samples_db); |
| |
| count_type count = 0; |
| |
| odb_node_nr_t node_nr, pos; |
| odb_node_t * node = odb_get_iterator(&samples_db, &node_nr); |
| for (pos = 0; pos < node_nr; ++pos) |
| count += node[pos].value; |
| |
| odb_close(&samples_db); |
| |
| return count; |
| } |
| |
| //static member |
| enum profile_type profile_t::is_spu_sample_file(string const & filename) |
| { |
| profile_type retval; |
| odb_t samples_db; |
| open_sample_file(filename, samples_db); |
| opd_header const & hdr = |
| *static_cast<opd_header *>(odb_get_data(&samples_db)); |
| retval = hdr.spu_profile ? cell_spu_profile: normal_profile; |
| odb_close(&samples_db); |
| return retval; |
| } |
| |
| //static member |
| void profile_t::open_sample_file(string const & filename, odb_t & db) |
| { |
| // Check first if the sample file version is ok else odb_open() can |
| // fail and the error message will be obscure. |
| opd_header head = read_header(filename); |
| |
| if (head.version != OPD_VERSION) { |
| ostringstream os; |
| os << "oprofpp: samples files version mismatch, are you " |
| << "running a daemon and post-profile tools with version " |
| << "mismatch ?\n"; |
| throw op_fatal_error(os.str()); |
| } |
| |
| int rc = odb_open(&db, filename.c_str(), ODB_RDONLY, |
| sizeof(struct opd_header)); |
| |
| if (rc) |
| throw op_fatal_error(filename + ": " + strerror(rc)); |
| } |
| |
| void profile_t::add_sample_file(string const & filename) |
| { |
| odb_t samples_db; |
| |
| open_sample_file(filename, samples_db); |
| |
| opd_header const & head = |
| *static_cast<opd_header *>(odb_get_data(&samples_db)); |
| |
| // if we already read a sample file header pointer is non null |
| if (file_header.get()) |
| op_check_header(head, *file_header, filename); |
| else |
| file_header.reset(new opd_header(head)); |
| |
| odb_node_nr_t node_nr, pos; |
| odb_node_t * node = odb_get_iterator(&samples_db, &node_nr); |
| |
| for (pos = 0; pos < node_nr; ++pos) { |
| ordered_samples_t::iterator it = |
| ordered_samples.find(node[pos].key); |
| if (it != ordered_samples.end()) { |
| it->second += node[pos].value; |
| } else { |
| ordered_samples_t::value_type |
| val(node[pos].key, node[pos].value); |
| ordered_samples.insert(val); |
| } |
| } |
| |
| odb_close(&samples_db); |
| } |
| |
| |
| void profile_t::set_offset(op_bfd const & abfd) |
| { |
| // if no bfd file has been located for this samples file, we can't |
| // shift sample because abfd.get_symbol_range() return the whole |
| // address space and setting a non zero start_offset will overflow |
| // in get_symbol_range() caller. |
| if (abfd.valid()) { |
| opd_header const & header = get_header(); |
| if (header.anon_start) { |
| start_offset = header.anon_start; |
| } else if (header.is_kernel) { |
| start_offset = abfd.get_start_offset(0); |
| } |
| } |
| cverb << (vdebug) << "start_offset is now " << start_offset << endl; |
| } |
| |
| |
| profile_t::iterator_pair |
| profile_t::samples_range(odb_key_t start, odb_key_t end) const |
| { |
| // Check the start position isn't before start_offset: |
| // this avoids wrapping/underflowing start/end. |
| // This can happen on e.g. ARM kernels, where .init is |
| // mapped before .text - we just have to skip any such |
| // .init symbols. |
| if (start < start_offset) { |
| return make_pair(const_iterator(ordered_samples.end(), 0), |
| const_iterator(ordered_samples.end(), 0)); |
| } |
| |
| start -= start_offset; |
| end -= start_offset; |
| |
| // sanity check if start > end caller will enter into an infinite loop |
| if (start > end) { |
| throw op_fatal_error("profile_t::samples_range(): start > end" |
| " something wrong with kernel or module layout ?\n" |
| "please report problem to " |
| "oprofile-list@lists.sourceforge.net"); |
| } |
| |
| ordered_samples_t::const_iterator first = |
| ordered_samples.lower_bound(start); |
| ordered_samples_t::const_iterator last = |
| ordered_samples.lower_bound(end); |
| |
| return make_pair(const_iterator(first, start_offset), |
| const_iterator(last, start_offset)); |
| } |
| |
| |
| profile_t::iterator_pair profile_t::samples_range() const |
| { |
| ordered_samples_t::const_iterator first = ordered_samples.begin(); |
| ordered_samples_t::const_iterator last = ordered_samples.end(); |
| |
| return make_pair(const_iterator(first, start_offset), |
| const_iterator(last, start_offset)); |
| } |