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