opgprof.cpp

Go to the documentation of this file.
00001 
00012 #include <iostream>
00013 #include <cstdio>
00014 
00015 #include "op_header.h"
00016 #include "profile.h"
00017 #include "op_libiberty.h"
00018 #include "op_fileio.h"
00019 #include "string_filter.h"
00020 #include "profile_container.h"
00021 #include "arrange_profiles.h"
00022 #include "image_errors.h"
00023 #include "opgprof_options.h"
00024 #include "cverb.h"
00025 #include "op_file.h"
00026 
00027 using namespace std;
00028 
00029 extern profile_classes classes;
00030 
00031 namespace {
00032 
00033 #define GMON_VERSION 1
00034 #define GMON_TAG_TIME_HIST 0
00035 #define GMON_TAG_CG_ARC 1
00036 
00037 struct gmon_hdr {
00038     char cookie[4];
00039     u32 version;
00040     u32 spare[3];
00041 };
00042 
00043 
00044 void op_write_vma(FILE * fp, op_bfd const & abfd, bfd_vma vma)
00045 {
00046     // bfd vma write size is a per binary property not a bfd
00047     // configuration property
00048     switch (abfd.bfd_arch_bits_per_address()) {
00049         case 32:
00050             op_write_u32(fp, vma);
00051             break;
00052         case 64:
00053             op_write_u64(fp, vma);
00054             break;
00055         default:
00056             cerr << "oprofile: unknown vma size for this binary\n";
00057             exit(EXIT_FAILURE);
00058     }
00059 }
00060 
00061 
00062 void get_vma_range(bfd_vma & min, bfd_vma & max,
00063                    profile_container const & samples)
00064 {
00065     min = bfd_vma(-1);
00066     max = 0;
00067 
00068     sample_container::samples_iterator it  = samples.begin();
00069     sample_container::samples_iterator end = samples.end();
00070     for (; it != end ; ++it) {
00071         if (it->second.vma < min)
00072             min = it->second.vma;
00073         if (it->second.vma > max)
00074             max = it->second.vma;
00075     }
00076 
00077     if (min == bfd_vma(-1))
00078         min = 0;
00079     // we must return a range [min, max) not a range [min, max]
00080     if (max != 0)
00081         max += 1;
00082 }
00083 
00084 
00095 bool aligned_samples(profile_container const & samples, int gap)
00096 {
00097     sample_container::samples_iterator it  = samples.begin();
00098     sample_container::samples_iterator end = samples.end();
00099     for (; it != end ; ++it) {
00100         if (it->second.vma % gap)
00101             return false;
00102     }
00103 
00104     return true;
00105 }
00106 
00107 
00108 void output_cg(FILE * fp, op_bfd const & abfd, profile_t const & cg_db)
00109 {
00110     opd_header const & header = cg_db.get_header();
00111     bfd_vma offset = 0;
00112     if (header.is_kernel)
00113         offset = abfd.get_start_offset(0);
00114     else
00115         offset = header.anon_start;
00116  
00117     profile_t::iterator_pair p_it = cg_db.samples_range();
00118     for (; p_it.first != p_it.second; ++p_it.first) {
00119         bfd_vma from = p_it.first.vma() >> 32;
00120         bfd_vma to = p_it.first.vma() & 0xffffffff;
00121 
00122         op_write_u8(fp, GMON_TAG_CG_ARC);
00123         op_write_vma(fp, abfd, abfd.offset_to_pc(from + offset));
00124         op_write_vma(fp, abfd, abfd.offset_to_pc(to + offset));
00125         u32 count = p_it.first.count();
00126         if (count != p_it.first.count()) {
00127             count = (u32)-1;
00128             cerr << "Warning: capping sample count by "
00129                  << p_it.first.count() - count << endl;
00130         }
00131         op_write_u32(fp, p_it.first.count());
00132     }
00133 }
00134 
00135 
00136 void output_gprof(op_bfd const & abfd, profile_container const & samples,
00137                   profile_t const & cg_db, string const & gmon_filename)
00138 {
00139     static gmon_hdr hdr = { { 'g', 'm', 'o', 'n' }, GMON_VERSION, {0, 0, 0 } };
00140 
00141     bfd_vma low_pc;
00142     bfd_vma high_pc;
00143 
00144     /* FIXME worth to try more multiplier ? */
00145     int multiplier = 2;
00146     if (aligned_samples(samples, 4))
00147         multiplier = 8;
00148 
00149     cverb << vdebug << "opgrof multiplier: " << multiplier << endl;
00150 
00151     get_vma_range(low_pc, high_pc, samples);
00152 
00153     cverb << vdebug << "low_pc: " << hex << low_pc << " " << "high_pc: "
00154           << high_pc << dec << endl;
00155 
00156     // round-down low_pc to ensure bin number is correct in the inner loop
00157     low_pc = (low_pc / multiplier) * multiplier;
00158     // round-up high_pc to ensure a correct histsize calculus
00159     high_pc = ((high_pc + multiplier - 1) / multiplier) * multiplier;
00160 
00161     cverb << vdebug << "low_pc: " << hex << low_pc << " " << "high_pc: "
00162           << high_pc << dec << endl;
00163 
00164     size_t histsize = (high_pc - low_pc) / multiplier;
00165 
00166     // FIXME: must we skip the flat profile write if histsize == 0 ?
00167     // (this can occur with callgraph w/o samples to the binary) but in
00168     // this case user must gprof --no-flat-profile which is a bit boring
00169     // and result *seems* weirds.
00170 
00171     FILE * fp = op_open_file(gmon_filename.c_str(), "w");
00172 
00173     op_write_file(fp, &hdr, sizeof(gmon_hdr));
00174     op_write_u8(fp, GMON_TAG_TIME_HIST);
00175 
00176     op_write_vma(fp, abfd, low_pc);
00177     op_write_vma(fp, abfd, high_pc);
00178     /* size of histogram */
00179     op_write_u32(fp, histsize);
00180     /* profiling rate */
00181     op_write_u32(fp, 1);
00182     op_write_file(fp, "samples\0\0\0\0\0\0\0\0", 15);
00183     /* abbreviation */
00184     op_write_u8(fp, '1');
00185 
00186     u16 * hist = (u16*)xcalloc(histsize, sizeof(u16));
00187 
00188     profile_container::symbol_choice choice;
00189     choice.threshold = options::threshold;
00190     symbol_collection symbols = samples.select_symbols(choice);
00191 
00192     symbol_collection::const_iterator sit = symbols.begin();
00193     symbol_collection::const_iterator send = symbols.end();
00194 
00195     for (; sit != send; ++sit) {
00196         sample_container::samples_iterator it  = samples.begin(*sit);
00197         sample_container::samples_iterator end = samples.end(*sit);
00198         for (; it != end ; ++it) {
00199             u32 pos = (it->second.vma - low_pc) / multiplier;
00200             count_type count = it->second.counts[0];
00201 
00202             if (pos >= histsize) {
00203                 cerr << "Bogus histogram bin " << pos
00204                      << ", larger than " << pos << " !\n";
00205                 continue;
00206             }
00207     
00208             if (hist[pos] + count > (u16)-1) {
00209                 hist[pos] = (u16)-1;
00210                 cerr << "Warning: capping sample count by "
00211                      << hist[pos] + count - ((u16)-1) << endl;
00212             } else {
00213                 hist[pos] += (u16)count;
00214             }
00215         }
00216     }
00217 
00218     op_write_file(fp, hist, histsize * sizeof(u16));
00219 
00220     if (!cg_db.empty())
00221         output_cg(fp, abfd, cg_db);
00222 
00223     op_close_file(fp);
00224 
00225     free(hist);
00226 }
00227 
00228 
00229 void
00230 load_samples(op_bfd const & abfd, list<profile_sample_files> const & files,
00231                   string const & image, profile_container & samples)
00232 {
00233     list<profile_sample_files>::const_iterator it = files.begin();
00234     list<profile_sample_files>::const_iterator const end = files.end();
00235 
00236     for (; it != end; ++it) {
00237         // we can get call graph w/o any samples to the binary
00238         if (it->sample_filename.empty())
00239             continue;
00240             
00241         cverb << vsfile << "loading flat samples files : "
00242               << it->sample_filename << endl;
00243 
00244         profile_t profile;
00245 
00246         profile.add_sample_file(it->sample_filename);
00247         profile.set_offset(abfd);
00248 
00249         check_mtime(abfd.get_filename(), profile.get_header());
00250 
00251         samples.add(profile, abfd, image, 0);
00252     }
00253 }
00254 
00255 
00256 void load_cg(profile_t & cg_db, list<profile_sample_files> const & files)
00257 {
00258     list<profile_sample_files>::const_iterator it = files.begin();
00259     list<profile_sample_files>::const_iterator const end = files.end();
00260 
00261     /* the list of non cg files is a super set of the list of cg file
00262      * (module always log a samples to non-cg files before logging
00263      * call stack) so by using the list of non-cg file we are sure to get
00264      * all existing cg files.
00265      */
00266     for (; it != end; ++it) {
00267         list<string>::const_iterator cit;
00268         list<string>::const_iterator const cend = it->cg_files.end();
00269         for (cit = it->cg_files.begin(); cit != cend; ++cit) {
00270             // FIXME: do we need filtering ?
00271             /* We can't handle start_offset now but after splitting
00272              * data in from/to eip. */
00273             cverb << vsfile << "loading cg samples file : " 
00274                   << *cit << endl;
00275             cg_db.add_sample_file(*cit);
00276         }
00277     }
00278 }
00279 
00280 
00281 int opgprof(options::spec const & spec)
00282 {
00283     handle_options(spec);
00284 
00285     profile_container samples(false, true, classes.extra_found_images);
00286 
00287     bool ok = image_profile.error == image_ok;
00288     // FIXME: symbol_filter would be allowed through option
00289     op_bfd abfd(image_profile.image, string_filter(),
00290             classes.extra_found_images, ok);
00291     if (!ok && image_profile.error == image_ok)
00292         image_profile.error = image_format_failure;
00293 
00294     if (image_profile.error != image_ok) {
00295         report_image_error(image_profile, true,
00296                    classes.extra_found_images);
00297         exit(EXIT_FAILURE);
00298     }
00299 
00300     profile_t cg_db;
00301     
00302     image_group_set const & groups = image_profile.groups[0];
00303     image_group_set::const_iterator it;
00304     for (it = groups.begin(); it != groups.end(); ++it) {
00305         load_samples(abfd, it->files, image_profile.image, samples);
00306 
00307         load_cg(cg_db, it->files);
00308     }
00309 
00310     output_gprof(abfd, samples, cg_db, options::gmon_filename);
00311 
00312     return 0;
00313 }
00314 
00315 
00316 } // anonymous namespace
00317 
00318 
00319 int main(int argc, char const * argv[])
00320 {
00321     return run_pp_tool(argc, argv, opgprof);
00322 }

Generated on 8 Nov 2012 for Oprofile by  doxygen 1.6.1