Home | History | Annotate | Download | only in pp
      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 
     27 using namespace std;
     28 
     29 extern profile_classes classes;
     30 
     31 namespace {
     32 
     33 #define GMON_VERSION 1
     34 #define GMON_TAG_TIME_HIST 0
     35 #define GMON_TAG_CG_ARC 1
     36 
     37 struct gmon_hdr {
     38 	char cookie[4];
     39 	u32 version;
     40 	u32 spare[3];
     41 };
     42 
     43 
     44 void 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 
     62 void 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  */
     95 bool 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 
    108 void 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 
    136 void 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 
    229 void
    230 load_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 
    256 void 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 
    281 int 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 
    319 int main(int argc, char const * argv[])
    320 {
    321 	return run_pp_tool(argc, argv, opgprof);
    322 }
    323