callgraph_container.cpp revision 8cfa702f803c5ef6a2b062a489a1b2cf66b45b5e
1/**
2 * @file callgraph_container.cpp
3 * Container associating symbols and caller/caller symbols
4 *
5 * @remark Copyright 2004 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Philippe Elie
9 * @author John Levon
10 */
11
12#include <cstdlib>
13
14#include <map>
15#include <set>
16#include <algorithm>
17#include <iterator>
18#include <string>
19#include <iostream>
20#include <numeric>
21
22#include "callgraph_container.h"
23#include "cverb.h"
24#include "parse_filename.h"
25#include "profile_container.h"
26#include "arrange_profiles.h"
27#include "populate.h"
28#include "string_filter.h"
29#include "op_bfd.h"
30#include "op_sample_file.h"
31#include "locate_images.h"
32
33using namespace std;
34
35namespace {
36
37bool operator==(cg_symbol const & lhs, cg_symbol const & rhs)
38{
39	less_symbol cmp_symb;
40	return !cmp_symb(lhs, rhs) && !cmp_symb(rhs, lhs);
41}
42
43
44// we store {caller,callee} inside a single u64
45odb_key_t caller_to_key(u32 value)
46{
47	return odb_key_t(value) << 32;
48}
49
50
51u32 key_to_callee(odb_key_t key)
52{
53	return key & 0xffffffff;
54}
55
56
57bool compare_by_callee_vma(pair<odb_key_t, count_type> const & lhs,
58                           pair<odb_key_t, count_type> const & rhs)
59{
60	return (key_to_callee(lhs.first)) < (key_to_callee(rhs.first));
61}
62
63
64/*
65 * We need 2 comparators for callgraph to get the desired output:
66 *
67 *	caller_with_few_samples
68 *	caller_with_many_samples
69 * function_with_many_samples
70 *	callee_with_many_samples
71 *	callee_with_few_samples
72 */
73
74bool
75compare_arc_count(symbol_entry const & lhs, symbol_entry const & rhs)
76{
77	return lhs.sample.counts[0] < rhs.sample.counts[0];
78}
79
80
81bool
82compare_arc_count_reverse(symbol_entry const & lhs, symbol_entry const & rhs)
83{
84	return rhs.sample.counts[0] < lhs.sample.counts[0];
85}
86
87
88// find the nearest bfd symbol for the given file offset and check it's
89// in range
90op_bfd_symbol const *
91get_symbol_by_filepos(op_bfd const & bfd, u32 bfd_offset,
92                      vma_t offset, symbol_index_t & i)
93{
94	offset += bfd_offset;
95	op_bfd_symbol tmpsym(offset, 0, string());
96
97	// sorted by filepos so this will find the nearest
98	vector<op_bfd_symbol>::const_iterator it =
99		upper_bound(bfd.syms.begin(), bfd.syms.end(), tmpsym);
100
101	if (it != bfd.syms.begin())
102		--it;
103
104	if (it == bfd.syms.end()) {
105		cerr << "get_symbol_by_filepos: no symbols at all?" << endl;
106		abort();
107	}
108
109	// if the offset is past the end of the symbol, we didn't find one
110	u32 const end_offset = it->size() + it->filepos();
111
112	if (offset >= end_offset) {
113		// let's be verbose for now
114		cerr << "warning: dropping hyperspace sample at offset "
115		     << hex << offset << " >= " << end_offset
116		     << " for binary " << bfd.get_filename() << dec << endl;
117		return NULL;
118	}
119
120	i = distance(bfd.syms.begin(), it);
121	return &(*it);
122}
123
124
125/// temporary caller and callee data held during processing
126class call_data {
127public:
128	call_data(profile_container const & p, profile_t const & pr,
129	          op_bfd const & bfd, u32 boff, image_name_id iid,
130	          image_name_id aid, bool debug_info)
131		: pc(p), profile(pr), b(bfd), boffset(boff), image(iid),
132		  app(aid), debug(debug_info) {}
133
134	/// point to a caller symbol
135	void caller_sym(symbol_index_t i) {
136		sym = symbol_entry();
137
138		unsigned long long start;
139		unsigned long long end;
140		b.get_symbol_range(i, start, end);
141
142		samples.clear();
143
144		// see profile_t::samples_range() for why we need this check
145		if (start > boffset) {
146			profile_t::iterator_pair p_it = profile.samples_range(
147				caller_to_key(start - boffset),
148				caller_to_key(end - boffset));
149
150			// Our odb_key_t contain (from_eip << 32 | to_eip),
151			// the range of keys we selected above contains one
152			// caller but different callees, and due to the
153			// ordering callee offsets are not consecutive: so
154			// we must sort them first.
155
156			for (; p_it.first != p_it.second; ++p_it.first) {
157				samples.push_back(make_pair(p_it.first.vma(),
158					p_it.first.count()));
159			}
160
161			sort(samples.begin(), samples.end(),
162			     compare_by_callee_vma);
163		}
164
165		sym.size = end - start;
166		sym.name = symbol_names.create(b.syms[i].name());
167		sym.sample.vma = b.syms[i].vma();
168
169		finish_sym(i, start);
170
171		if (cverb << vdebug) {
172			cverb << vdebug << hex << "Caller sym: "
173			      << b.syms[i].name() << " filepos " << start
174			      << "-" << end << dec << endl;
175		}
176	}
177
178	/// point to a callee symbol
179	bool callee_sym(u32 off) {
180		sym = symbol_entry();
181
182		symbol_index_t i = 0;
183		op_bfd_symbol const * bfdsym =
184			get_symbol_by_filepos(b, boffset, off, i);
185
186		if (!bfdsym)
187			return false;
188
189		callee_end = bfdsym->size() + bfdsym->filepos() - boffset;
190
191		sym.size = bfdsym->size();
192		sym.name = symbol_names.create(bfdsym->name());
193		sym.sample.vma = bfdsym->vma();
194
195		finish_sym(i, bfdsym->filepos());
196
197		if (cverb << vdebug) {
198			cverb << vdebug << hex << "Callee sym: "
199			      << bfdsym->name() << " filepos "
200			      << bfdsym->filepos() << "-"
201			      << (bfdsym->filepos() + bfdsym->size())
202			      << dec << endl;
203		}
204		return true;
205	}
206
207	void verbose_bfd(string const & prefix) const {
208		cverb << vdebug << prefix << " " << b.get_filename()
209		      << " offset " << boffset << " app "
210		      << image_names.name(app) << endl;
211	}
212
213	typedef vector<pair<odb_key_t, count_type> > samples_t;
214
215	typedef samples_t::const_iterator const_iterator;
216
217	samples_t samples;
218	symbol_entry sym;
219	u32 callee_end;
220
221private:
222	/// fill in the rest of the sym
223	void finish_sym(symbol_index_t i, unsigned long start) {
224		sym.image_name = image;
225		sym.app_name = app;
226		symbol_entry const * self = pc.find(sym);
227		if (self)
228			sym.sample.counts = self->sample.counts;
229
230		if (debug) {
231			string filename;
232			file_location & loc = sym.sample.file_loc;
233			if (b.get_linenr(i, start, filename, loc.linenr))
234				loc.filename = debug_names.create(filename);
235		}
236	}
237
238	profile_container const & pc;
239	profile_t const & profile;
240	op_bfd const & b;
241	u32 boffset;
242	image_name_id image;
243	image_name_id app;
244	bool debug;
245};
246
247
248/// accumulate all samples for a given caller/callee pair
249count_type
250accumulate_callee(call_data::const_iterator & it, call_data::const_iterator end,
251                  u32 callee_end)
252{
253	count_type count = 0;
254	call_data::const_iterator const start = it;
255
256	while (it != end) {
257		u32 offset = key_to_callee(it->first);
258
259		if (cverb << (vdebug & vlevel1)) {
260			cverb << (vdebug & vlevel1) << hex << "offset: "
261			      << offset << dec << endl;
262		}
263
264		// stop if we pass the end of the callee
265		if (offset >= callee_end)
266			break;
267
268		count += it->second;
269		++it;
270	}
271
272	// If we haven't advanced at all, then we'll get
273	// an infinite loop, so we must abort.
274	if (it == start) {
275		cerr << "failure to advance iterator\n";
276		abort();
277	}
278
279	return count;
280}
281
282
283} // anonymous namespace
284
285
286void arc_recorder::
287add(symbol_entry const & caller, symbol_entry const * callee,
288    count_array_t const & arc_count)
289{
290	cg_data & data = sym_map[caller];
291
292	// If we have a callee, add it to the caller's list, then
293	// add the caller to the callee's list.
294	if (callee) {
295		data.callees[*callee] += arc_count;
296
297		cg_data & callee_data = sym_map[*callee];
298
299		callee_data.callers[caller] += arc_count;
300	}
301}
302
303
304void arc_recorder::process_children(cg_symbol & sym, double threshold)
305{
306	// generate the synthetic self entry for the symbol
307	symbol_entry self = sym;
308
309	self.name = symbol_names.create(symbol_names.demangle(self.name)
310	                                + " [self]");
311
312	sym.total_callee_count += self.sample.counts;
313	sym.callees.push_back(self);
314
315	sort(sym.callers.begin(), sym.callers.end(), compare_arc_count);
316	sort(sym.callees.begin(), sym.callees.end(), compare_arc_count_reverse);
317
318	// FIXME: this relies on sort always being sample count
319
320	cg_symbol::children::iterator cit = sym.callers.begin();
321	cg_symbol::children::iterator cend = sym.callers.end();
322
323	while (cit != cend && op_ratio(cit->sample.counts[0],
324	       sym.total_caller_count[0]) < threshold)
325		++cit;
326
327	if (cit != cend)
328		sym.callers.erase(sym.callers.begin(), cit);
329
330	cit = sym.callees.begin();
331	cend = sym.callees.end();
332
333	while (cit != cend && op_ratio(cit->sample.counts[0],
334	       sym.total_callee_count[0]) >= threshold)
335		++cit;
336
337	if (cit != cend)
338		sym.callees.erase(cit, sym.callees.end());
339}
340
341
342void arc_recorder::
343process(count_array_t total, double threshold,
344        string_filter const & sym_filter)
345{
346	map_t::const_iterator it;
347	map_t::const_iterator end = sym_map.end();
348
349	for (it = sym_map.begin(); it != end; ++it) {
350		cg_symbol sym((*it).first);
351		cg_data const & data = (*it).second;
352
353		// threshold out the main symbol if needed
354		if (op_ratio(sym.sample.counts[0], total[0]) < threshold)
355			continue;
356
357		// FIXME: slow?
358		if (!sym_filter.match(symbol_names.demangle(sym.name)))
359			continue;
360
361		cg_data::children::const_iterator cit;
362		cg_data::children::const_iterator cend = data.callers.end();
363
364		for (cit = data.callers.begin(); cit != cend; ++cit) {
365			symbol_entry csym = cit->first;
366			csym.sample.counts = cit->second;
367			sym.callers.push_back(csym);
368			sym.total_caller_count += cit->second;
369		}
370
371		cend = data.callees.end();
372
373		for (cit = data.callees.begin(); cit != cend; ++cit) {
374			symbol_entry csym = cit->first;
375			csym.sample.counts = cit->second;
376			sym.callees.push_back(csym);
377			sym.total_callee_count += cit->second;
378		}
379
380		process_children(sym, threshold);
381
382		// insert sym into cg_syms_objs
383		// then store pointer to sym in cg_syms
384		cg_syms.push_back(&(*cg_syms_objs.insert(cg_syms_objs.end(), sym)));
385	}
386}
387
388
389symbol_collection const & arc_recorder::get_symbols() const
390{
391	return cg_syms;
392}
393
394
395void callgraph_container::populate(list<inverted_profile> const & iprofiles,
396   extra_images const & extra, bool debug_info, double threshold,
397   bool merge_lib, string_filter const & sym_filter)
398{
399	this->extra_found_images = extra;
400	// non callgraph samples container, we record sample at symbol level
401	// not at vma level.
402	profile_container pc(debug_info, false, extra_found_images);
403
404	list<inverted_profile>::const_iterator it;
405	list<inverted_profile>::const_iterator const end = iprofiles.end();
406	for (it = iprofiles.begin(); it != end; ++it) {
407		// populate_caller_image take care about empty sample filename
408		populate_for_image(pc, *it, sym_filter, 0);
409	}
410
411	add_symbols(pc);
412
413	total_count = pc.samples_count();
414
415	for (it = iprofiles.begin(); it != end; ++it) {
416		for (size_t i = 0; i < it->groups.size(); ++i) {
417			populate(it->groups[i], it->image,
418				i, pc, debug_info, merge_lib);
419		}
420	}
421
422	recorder.process(total_count, threshold / 100.0, sym_filter);
423}
424
425
426void callgraph_container::populate(list<image_set> const & lset,
427	string const & app_image, size_t pclass,
428	profile_container const & pc, bool debug_info, bool merge_lib)
429{
430	list<image_set>::const_iterator lit;
431	list<image_set>::const_iterator const lend = lset.end();
432	for (lit = lset.begin(); lit != lend; ++lit) {
433		list<profile_sample_files>::const_iterator pit;
434		list<profile_sample_files>::const_iterator pend
435			= lit->files.end();
436		for (pit = lit->files.begin(); pit != pend; ++pit) {
437			populate(pit->cg_files, app_image,
438				 pclass, pc, debug_info, merge_lib);
439		}
440	}
441}
442
443
444void callgraph_container::populate(list<string> const & cg_files,
445	string const & app_image, size_t pclass,
446	profile_container const & pc, bool debug_info, bool merge_lib)
447{
448	list<string>::const_iterator it;
449	list<string>::const_iterator const end = cg_files.end();
450	for (it = cg_files.begin(); it != end; ++it) {
451		cverb << vdebug << "samples file : " << *it << endl;
452
453		parsed_filename caller_file =
454			parse_filename(*it, extra_found_images);
455		string const app_name = caller_file.image;
456
457		image_error error;
458		extra_found_images.find_image_path(caller_file.lib_image,
459				error, false);
460
461		if (error != image_ok)
462			report_image_error(caller_file.lib_image,
463					   error, false, extra_found_images);
464
465		bool caller_bfd_ok = true;
466		op_bfd caller_bfd(caller_file.lib_image,
467			string_filter(), extra_found_images, caller_bfd_ok);
468		if (!caller_bfd_ok)
469			report_image_error(caller_file.lib_image,
470			                   image_format_failure, false,
471					   extra_found_images);
472
473		parsed_filename callee_file =
474			parse_filename(*it, extra_found_images);
475
476		extra_found_images.find_image_path(callee_file.cg_image,
477				error, false);
478		if (error != image_ok)
479			report_image_error(callee_file.cg_image,
480					   error, false, extra_found_images);
481
482		bool callee_bfd_ok = true;
483		op_bfd callee_bfd(callee_file.cg_image,
484			string_filter(), extra_found_images, callee_bfd_ok);
485		if (!callee_bfd_ok)
486			report_image_error(callee_file.cg_image,
487		                           image_format_failure, false,
488					   extra_found_images);
489
490		profile_t profile;
491		// We can't use start_offset support in profile_t, give
492		// it a zero offset and we will fix that in add()
493		profile.add_sample_file(*it);
494		add(profile, caller_bfd, caller_bfd_ok, callee_bfd,
495		    merge_lib ? app_image : app_name, pc,
496		    debug_info, pclass);
497	}
498}
499
500
501void callgraph_container::
502add(profile_t const & profile, op_bfd const & caller_bfd, bool caller_bfd_ok,
503   op_bfd const & callee_bfd, string const & app_name,
504   profile_container const & pc, bool debug_info, size_t pclass)
505{
506	string const image_name = caller_bfd.get_filename();
507
508	opd_header const & header = profile.get_header();
509
510	// We can't use kernel sample file w/o the binary else we will
511	// use it with a zero offset, the code below will abort because
512	// we will get incorrect callee sub-range and out of range
513	// callee vma. FIXME
514	if (header.is_kernel && !caller_bfd_ok)
515		return;
516
517	// We must handle start_offset, this offset can be different for the
518	// caller and the callee: kernel sample traversing the syscall barrier.
519	u32 caller_offset;
520	if (header.is_kernel)
521		caller_offset = caller_bfd.get_start_offset(0);
522	else
523		caller_offset = header.anon_start;
524
525	u32 callee_offset;
526	if (header.cg_to_is_kernel)
527		callee_offset = callee_bfd.get_start_offset(0);
528	else
529		callee_offset = header.cg_to_anon_start;
530
531	image_name_id image_id = image_names.create(image_name);
532	image_name_id callee_image_id = image_names.create(callee_bfd.get_filename());
533	image_name_id app_id = image_names.create(app_name);
534
535	call_data caller(pc, profile, caller_bfd, caller_offset, image_id,
536	                   app_id, debug_info);
537	call_data callee(pc, profile, callee_bfd, callee_offset,
538	                   callee_image_id, app_id, debug_info);
539
540	if (cverb << vdebug) {
541		caller.verbose_bfd("Caller:");
542		callee.verbose_bfd("Callee:");
543	}
544
545	// For each symbol in the caller bfd, process all arcs to
546	// callee bfd symbols
547
548	for (symbol_index_t i = 0; i < caller_bfd.syms.size(); ++i) {
549
550		caller.caller_sym(i);
551
552		call_data::const_iterator dit = caller.samples.begin();
553		call_data::const_iterator dend = caller.samples.end();
554		while (dit != dend) {
555			// if we can't find the callee, skip an arc
556			if (!callee.callee_sym(key_to_callee(dit->first))) {
557				++dit;
558				continue;
559			}
560
561			count_array_t arc_count;
562			arc_count[pclass] =
563				accumulate_callee(dit, dend, callee.callee_end);
564
565			recorder.add(caller.sym, &callee.sym, arc_count);
566		}
567	}
568}
569
570
571void callgraph_container::add_symbols(profile_container const & pc)
572{
573	symbol_container::symbols_t::iterator it;
574	symbol_container::symbols_t::iterator const end = pc.end_symbol();
575
576	for (it = pc.begin_symbol(); it != end; ++it)
577		recorder.add(*it, 0, count_array_t());
578}
579
580
581column_flags callgraph_container::output_hint() const
582{
583	column_flags output_hints = cf_none;
584
585	// FIXME: costly: must we access directly recorder map ?
586	symbol_collection syms = recorder.get_symbols();
587
588	symbol_collection::iterator it;
589	symbol_collection::iterator const end = syms.end();
590	for (it = syms.begin(); it != end; ++it)
591		output_hints = (*it)->output_hint(output_hints);
592
593	return output_hints;
594}
595
596
597count_array_t callgraph_container::samples_count() const
598{
599	return total_count;
600}
601
602
603symbol_collection const & callgraph_container::get_symbols() const
604{
605	return recorder.get_symbols();
606}
607