blob: b9b1a4926c980063f9dd0a455048120a5f7f135f [file] [log] [blame]
Upstreamcc2ee171970-01-12 13:46:40 +00001/**
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, odb_value_t> const & lhs,
58 pair<odb_key_t, odb_value_t> 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 start;
139 unsigned 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.sym_offset(i, start) + 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;
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, odb_value_t> > 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
249u32
250accumulate_callee(call_data::const_iterator & it, call_data::const_iterator end,
251 u32 callee_end)
252{
253 u32 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 cg_syms.push_back(sym);
383 }
384}
385
386
387cg_collection arc_recorder::get_symbols() const
388{
389 return cg_syms;
390}
391
392
393void callgraph_container::populate(string const & archive_path,
394 list<inverted_profile> const & iprofiles,
395 extra_images const & extra, bool debug_info, double threshold,
396 bool merge_lib, string_filter const & sym_filter)
397{
398 // non callgraph samples container, we record sample at symbol level
399 // not at vma level.
400 profile_container pc(debug_info, false);
401
402 list<inverted_profile>::const_iterator it;
403 list<inverted_profile>::const_iterator const end = iprofiles.end();
404 for (it = iprofiles.begin(); it != end; ++it) {
405 // populate_caller_image take care about empty sample filename
406 populate_for_image(archive_path, pc, *it, sym_filter, 0);
407 }
408
409 add_symbols(pc);
410
411 total_count = pc.samples_count();
412
413 for (it = iprofiles.begin(); it != end; ++it) {
414 for (size_t i = 0; i < it->groups.size(); ++i) {
415 populate(archive_path, it->groups[i], it->image, extra,
416 i, pc, debug_info, merge_lib);
417 }
418 }
419
420 recorder.process(total_count, threshold / 100.0, sym_filter);
421}
422
423
424void callgraph_container::populate(string const & archive_path,
425 list<image_set> const & lset,
426 string const & app_image, extra_images const & extra, size_t pclass,
427 profile_container const & pc, bool debug_info, bool merge_lib)
428{
429 list<image_set>::const_iterator lit;
430 list<image_set>::const_iterator const lend = lset.end();
431 for (lit = lset.begin(); lit != lend; ++lit) {
432 list<profile_sample_files>::const_iterator pit;
433 list<profile_sample_files>::const_iterator pend
434 = lit->files.end();
435 for (pit = lit->files.begin(); pit != pend; ++pit) {
436 populate(archive_path, pit->cg_files, app_image,
437 extra, pclass, pc, debug_info, merge_lib);
438 }
439 }
440}
441
442
443void callgraph_container::populate(string const & archive_path,
444 list<string> const & cg_files,
445 string const & app_image, extra_images const & extra, 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 = parse_filename(*it);
454 string const app_name = caller_file.image;
455
456 image_error error;
457 string caller_binary =
458 find_image_path(archive_path, caller_file.lib_image,
459 extra, error);
460
461 if (error != image_ok)
462 report_image_error(archive_path + caller_file.lib_image,
463 error, false);
464
465 bool caller_bfd_ok = true;
466 op_bfd caller_bfd(archive_path, caller_binary,
467 string_filter(), caller_bfd_ok);
468 if (!caller_bfd_ok)
469 report_image_error(caller_binary,
470 image_format_failure, false);
471
472 parsed_filename callee_file = parse_filename(*it);
473
474 string callee_binary =
475 find_image_path(archive_path, callee_file.cg_image,
476 extra, error);
477 if (error != image_ok)
478 report_image_error(callee_file.cg_image, error, false);
479
480 bool callee_bfd_ok = true;
481 op_bfd callee_bfd(archive_path, callee_binary,
482 string_filter(), callee_bfd_ok);
483 if (!callee_bfd_ok)
484 report_image_error(callee_binary,
485 image_format_failure, false);
486
487 profile_t profile;
488 // We can't use start_offset support in profile_t, give
489 // it a zero offset and we will fix that in add()
490 profile.add_sample_file(*it);
491 add(profile, caller_bfd, caller_bfd_ok, callee_bfd,
492 merge_lib ? app_image : app_name, pc,
493 debug_info, pclass);
494 }
495}
496
497
498void callgraph_container::
499add(profile_t const & profile, op_bfd const & caller_bfd, bool caller_bfd_ok,
500 op_bfd const & callee_bfd, string const & app_name,
501 profile_container const & pc, bool debug_info, size_t pclass)
502{
503 string const image_name = caller_bfd.get_filename();
504
505 opd_header const & header = profile.get_header();
506
507 // We can't use kernel sample file w/o the binary else we will
508 // use it with a zero offset, the code below will abort because
509 // we will get incorrect callee sub-range and out of range
510 // callee vma. FIXME
511 if (header.is_kernel && !caller_bfd_ok)
512 return;
513
514 // We must handle start_offset, this offset can be different for the
515 // caller and the callee: kernel sample traversing the syscall barrier.
516 u32 caller_offset = 0;
517
518 if (header.is_kernel || header.anon_start) {
519 caller_offset = caller_bfd.get_start_offset(header.anon_start);
520 }
521
522 u32 callee_offset = 0;
523
524 if (header.cg_to_is_kernel || header.cg_to_anon_start) {
525 callee_offset =
526 callee_bfd.get_start_offset(header.cg_to_anon_start);
527 }
528
529 image_name_id image_id = image_names.create(image_name);
530 image_name_id callee_image_id = image_names.create(callee_bfd.get_filename());
531 image_name_id app_id = image_names.create(app_name);
532
533 call_data caller(pc, profile, caller_bfd, caller_offset, image_id,
534 app_id, debug_info);
535 call_data callee(pc, profile, callee_bfd, callee_offset,
536 callee_image_id, app_id, debug_info);
537
538 if (cverb << vdebug) {
539 caller.verbose_bfd("Caller:");
540 callee.verbose_bfd("Callee:");
541 }
542
543 // For each symbol in the caller bfd, process all arcs to
544 // callee bfd symbols
545
546 for (symbol_index_t i = 0; i < caller_bfd.syms.size(); ++i) {
547
548 caller.caller_sym(i);
549
550 call_data::const_iterator dit = caller.samples.begin();
551 call_data::const_iterator dend = caller.samples.end();
552 while (dit != dend) {
553 // if we can't find the callee, skip an arc
554 if (!callee.callee_sym(key_to_callee(dit->first))) {
555 ++dit;
556 continue;
557 }
558
559 count_array_t arc_count;
560 arc_count[pclass] =
561 accumulate_callee(dit, dend, callee.callee_end);
562
563 recorder.add(caller.sym, &callee.sym, arc_count);
564 }
565 }
566}
567
568
569void callgraph_container::add_symbols(profile_container const & pc)
570{
571 symbol_container::symbols_t::iterator it;
572 symbol_container::symbols_t::iterator const end = pc.end_symbol();
573
574 for (it = pc.begin_symbol(); it != end; ++it)
575 recorder.add(*it, 0, count_array_t());
576}
577
578
579column_flags callgraph_container::output_hint() const
580{
581 column_flags output_hints = cf_none;
582
583 // FIXME: costly: must we access directly recorder map ?
584 cg_collection syms = recorder.get_symbols();
585
586 cg_collection::const_iterator it;
587 cg_collection::const_iterator const end = syms.end();
588 for (it = syms.begin(); it != end; ++it)
589 output_hints = it->output_hint(output_hints);
590
591 return output_hints;
592}
593
594
595count_array_t callgraph_container::samples_count() const
596{
597 return total_count;
598}
599
600
601cg_collection callgraph_container::get_symbols() const
602{
603 return recorder.get_symbols();
604}