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