opgprof.cpp revision cc2ee177dbb3befca43e36cfc56778b006c3d050
1/** 2 * @file opgprof.cpp 3 * Implement opgprof utility 4 * 5 * @remark Copyright 2003 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author John Levon 9 * @author Philippe Elie 10 */ 11 12#include <iostream> 13#include <cstdio> 14 15#include "op_header.h" 16#include "profile.h" 17#include "op_libiberty.h" 18#include "op_fileio.h" 19#include "string_filter.h" 20#include "profile_container.h" 21#include "arrange_profiles.h" 22#include "image_errors.h" 23#include "opgprof_options.h" 24#include "cverb.h" 25#include "op_file.h" 26 27using namespace std; 28 29namespace { 30 31#define GMON_VERSION 1 32#define GMON_TAG_TIME_HIST 0 33#define GMON_TAG_CG_ARC 1 34 35struct gmon_hdr { 36 char cookie[4]; 37 u32 version; 38 u32 spare[3]; 39}; 40 41 42void op_write_vma(FILE * fp, op_bfd const & abfd, bfd_vma vma) 43{ 44 // bfd vma write size is a per binary property not a bfd 45 // configuration property 46 switch (abfd.bfd_arch_bits_per_address()) { 47 case 32: 48 op_write_u32(fp, vma); 49 break; 50 case 64: 51 op_write_u64(fp, vma); 52 break; 53 default: 54 cerr << "oprofile: unknown vma size for this binary\n"; 55 exit(EXIT_FAILURE); 56 } 57} 58 59 60void get_vma_range(bfd_vma & min, bfd_vma & max, 61 profile_container const & samples) 62{ 63 min = bfd_vma(-1); 64 max = 0; 65 66 sample_container::samples_iterator it = samples.begin(); 67 sample_container::samples_iterator end = samples.end(); 68 for (; it != end ; ++it) { 69 if (it->second.vma < min) 70 min = it->second.vma; 71 if (it->second.vma > max) 72 max = it->second.vma; 73 } 74 75 if (min == bfd_vma(-1)) 76 min = 0; 77 // we must return a range [min, max) not a range [min, max] 78 if (max != 0) 79 max += 1; 80} 81 82 83/** 84 * @param abfd bfd object 85 * @param samples_files profile container to act on 86 * @param gap a power of 2 87 * 88 * return true if all sample in samples_files are at least aligned on gap. This 89 * function is used to get at runtime the right size of gprof bin size 90 * reducing gmon.out on arch with fixed size instruction length 91 * 92 */ 93bool aligned_samples(profile_container const & samples, int gap) 94{ 95 sample_container::samples_iterator it = samples.begin(); 96 sample_container::samples_iterator end = samples.end(); 97 for (; it != end ; ++it) { 98 if (it->second.vma % gap) 99 return false; 100 } 101 102 return true; 103} 104 105 106void output_cg(FILE * fp, op_bfd const & abfd, profile_t const & cg_db) 107{ 108 opd_header const & header = cg_db.get_header(); 109 bfd_vma offset = 0; 110 if (header.is_kernel || header.anon_start) 111 offset = abfd.get_start_offset(header.anon_start); 112 113 profile_t::iterator_pair p_it = cg_db.samples_range(); 114 for (; p_it.first != p_it.second; ++p_it.first) { 115 bfd_vma from = p_it.first.vma() >> 32; 116 bfd_vma to = p_it.first.vma() & 0xffffffff; 117 118 op_write_u8(fp, GMON_TAG_CG_ARC); 119 op_write_vma(fp, abfd, abfd.offset_to_pc(from + offset)); 120 op_write_vma(fp, abfd, abfd.offset_to_pc(to + offset)); 121 op_write_u32(fp, p_it.first.count()); 122 } 123} 124 125 126void output_gprof(op_bfd const & abfd, profile_container const & samples, 127 profile_t const & cg_db, string const & gmon_filename) 128{ 129 static gmon_hdr hdr = { { 'g', 'm', 'o', 'n' }, GMON_VERSION, {0, 0, 0 } }; 130 131 bfd_vma low_pc; 132 bfd_vma high_pc; 133 134 /* FIXME worth to try more multiplier ? */ 135 int multiplier = 2; 136 if (aligned_samples(samples, 4)) 137 multiplier = 8; 138 139 cverb << vdebug << "opgrof multiplier: " << multiplier << endl; 140 141 get_vma_range(low_pc, high_pc, samples); 142 143 cverb << vdebug << "low_pc: " << hex << low_pc << " " << "high_pc: " 144 << high_pc << dec << endl; 145 146 // round-down low_pc to ensure bin number is correct in the inner loop 147 low_pc = (low_pc / multiplier) * multiplier; 148 // round-up high_pc to ensure a correct histsize calculus 149 high_pc = ((high_pc + multiplier - 1) / multiplier) * multiplier; 150 151 cverb << vdebug << "low_pc: " << hex << low_pc << " " << "high_pc: " 152 << high_pc << dec << endl; 153 154 size_t histsize = (high_pc - low_pc) / multiplier; 155 156 // FIXME: must we skip the flat profile write if histsize == 0 ? 157 // (this can occur with callgraph w/o samples to the binary) but in 158 // this case user must gprof --no-flat-profile whiwh is a bit boring 159 // and result *seems* weirds. 160 161 FILE * fp = op_open_file(gmon_filename.c_str(), "w"); 162 163 op_write_file(fp, &hdr, sizeof(gmon_hdr)); 164 op_write_u8(fp, GMON_TAG_TIME_HIST); 165 166 op_write_vma(fp, abfd, low_pc); 167 op_write_vma(fp, abfd, high_pc); 168 /* size of histogram */ 169 op_write_u32(fp, histsize); 170 /* profiling rate */ 171 op_write_u32(fp, 1); 172 op_write_file(fp, "samples\0\0\0\0\0\0\0\0", 15); 173 /* abbreviation */ 174 op_write_u8(fp, '1'); 175 176 u16 * hist = (u16*)xcalloc(histsize, sizeof(u16)); 177 178 profile_container::symbol_choice choice; 179 choice.threshold = options::threshold; 180 symbol_collection symbols = samples.select_symbols(choice); 181 182 symbol_collection::const_iterator sit = symbols.begin(); 183 symbol_collection::const_iterator send = symbols.end(); 184 185 for (; sit != send; ++sit) { 186 sample_container::samples_iterator it = samples.begin(*sit); 187 sample_container::samples_iterator end = samples.end(*sit); 188 for (; it != end ; ++it) { 189 u32 pos = (it->second.vma - low_pc) / multiplier; 190 u32 count = it->second.counts[0]; 191 192 if (pos >= histsize) { 193 cerr << "Bogus histogram bin " << pos 194 << ", larger than " << pos << " !\n"; 195 continue; 196 } 197 198 if (hist[pos] + count > (u16)-1) { 199 hist[pos] = (u16)-1; 200 cerr << "Warning: capping sample count by " 201 << hist[pos] + count - ((u16)-1) << endl; 202 } else { 203 hist[pos] += (u16)count; 204 } 205 } 206 } 207 208 op_write_file(fp, hist, histsize * sizeof(u16)); 209 210 if (!cg_db.empty()) 211 output_cg(fp, abfd, cg_db); 212 213 op_close_file(fp); 214 215 free(hist); 216} 217 218 219void 220load_samples(op_bfd const & abfd, list<profile_sample_files> const & files, 221 string const & image, profile_container & samples) 222{ 223 list<profile_sample_files>::const_iterator it = files.begin(); 224 list<profile_sample_files>::const_iterator const end = files.end(); 225 226 for (; it != end; ++it) { 227 // we can get call graph w/o any samples to the binary 228 if (it->sample_filename.empty()) 229 continue; 230 231 cverb << vsfile << "loading flat samples files : " 232 << it->sample_filename << endl; 233 234 profile_t profile; 235 236 profile.add_sample_file(it->sample_filename); 237 profile.set_offset(abfd); 238 239 check_mtime(abfd.get_filename(), profile.get_header()); 240 241 samples.add(profile, abfd, image, 0); 242 } 243} 244 245 246void load_cg(profile_t & cg_db, list<profile_sample_files> const & files) 247{ 248 list<profile_sample_files>::const_iterator it = files.begin(); 249 list<profile_sample_files>::const_iterator const end = files.end(); 250 251 /* the list of non cg files is a super set of the list of cg file 252 * (module always log a samples to non-cg files before logging 253 * call stack) so by using the list of non-cg file we are sure to get 254 * all existing cg files. 255 */ 256 for (; it != end; ++it) { 257 list<string>::const_iterator cit; 258 list<string>::const_iterator const cend = it->cg_files.end(); 259 for (cit = it->cg_files.begin(); cit != cend; ++cit) { 260 // FIXME: do we need filtering ? 261 /* We can't handle start_offset now but after splitting 262 * data in from/to eip. */ 263 cverb << vsfile << "loading cg samples file : " 264 << *cit << endl; 265 cg_db.add_sample_file(*cit); 266 } 267 } 268} 269 270 271int opgprof(options::spec const & spec) 272{ 273 handle_options(spec); 274 275 profile_container samples(false, true); 276 277 bool ok = image_profile.error == image_ok; 278 // FIXME: symbol_filter would be allowed through option 279 op_bfd abfd(options::archive_path, image_profile.image, 280 string_filter(), ok); 281 if (!ok && image_profile.error == image_ok) 282 image_profile.error = image_format_failure; 283 284 if (image_profile.error != image_ok) { 285 report_image_error(image_profile, true); 286 exit(EXIT_FAILURE); 287 } 288 289 profile_t cg_db; 290 291 image_group_set const & groups = image_profile.groups[0]; 292 image_group_set::const_iterator it; 293 for (it = groups.begin(); it != groups.end(); ++it) { 294 load_samples(abfd, it->files, image_profile.image, samples); 295 296 load_cg(cg_db, it->files); 297 } 298 299 output_gprof(abfd, samples, cg_db, options::gmon_filename); 300 301 return 0; 302} 303 304 305} // anonymous namespace 306 307 308int main(int argc, char const * argv[]) 309{ 310 return run_pp_tool(argc, argv, opgprof); 311} 312