2
* @file format_output.cpp
3
* outputting format for symbol lists
5
* @remark Copyright 2002 OProfile authors
6
* @remark Read the file COPYING
8
* @author Philippe Elie
12
/* older glibc has C99 INFINITY in _GNU_SOURCE */
23
#include "string_manip.h"
24
#include "string_filter.h"
26
#include "format_output.h"
27
#include "profile_container.h"
28
#include "callgraph_container.h"
29
#include "diff_container.h"
30
#include "arrange_profiles.h"
31
#include "xml_output.h"
32
#include "xml_utils.h"
40
string const get_linenr_info(file_location const floc, bool lf)
44
string const & filename = lf
45
? debug_names.name(floc.filename)
46
: debug_names.basename(floc.filename);
48
if (!filename.empty())
49
out << filename << ":" << floc.linenr;
51
out << "(no location information)";
56
string get_vma(bfd_vma vma, bool vma_64)
59
int width = vma_64 ? 16 : 8;
61
out << hex << setw(width) << setfill('0') << vma;
66
string get_percent(count_type dividend, count_type divisor)
68
double ratio = op_ratio(dividend, divisor);
70
return ::format_percent(ratio * 100, percent_int_width,
74
bool extract_linenr_info(string const & info, string & file, size_t & line)
78
string::size_type colon_pos = info.find(":");
80
if (colon_pos == string::npos)
83
file = info.substr(0, colon_pos);
84
istringstream is_info(info.substr(colon_pos+1));
90
} // anonymous namespace
92
namespace format_output {
94
formatter::formatter(extra_images const & extra)
99
long_filenames(false),
101
extra_found_images(extra)
103
format_map[ff_vma] = field_description(9, "vma", &formatter::format_vma);
104
format_map[ff_nr_samples] = field_description(9, "samples", &formatter::format_nr_samples);
105
format_map[ff_nr_samples_cumulated] = field_description(14, "cum. samples", &formatter::format_nr_cumulated_samples);
106
format_map[ff_percent] = field_description(9, "%", &formatter::format_percent);
107
format_map[ff_percent_cumulated] = field_description(11, "cum. %", &formatter::format_cumulated_percent);
108
format_map[ff_linenr_info] = field_description(28, "linenr info", &formatter::format_linenr_info);
109
format_map[ff_image_name] = field_description(25, "image name", &formatter::format_image_name);
110
format_map[ff_app_name] = field_description(25, "app name", &formatter::format_app_name);
111
format_map[ff_symb_name] = field_description(30, "symbol name", &formatter::format_symb_name);
112
format_map[ff_percent_details] = field_description(9, "%", &formatter::format_percent_details);
113
format_map[ff_percent_cumulated_details] = field_description(10, "cum. %", &formatter::format_cumulated_percent_details);
114
format_map[ff_diff] = field_description(10, "diff %", &formatter::format_diff);
118
formatter::~formatter()
123
void formatter::set_nr_classes(size_t nr)
129
void formatter::add_format(format_flags flag)
131
flags = static_cast<format_flags>(flags | flag);
135
void formatter::show_header(bool on_off)
137
need_header = on_off;
141
void formatter::vma_format_64bit(bool on_off)
147
void formatter::show_long_filenames(bool on_off)
149
long_filenames = on_off;
153
void formatter::show_global_percent(bool on_off)
155
global_percent = on_off;
159
void formatter::output_header(ostream & out)
166
// first output the vma field
168
padding = output_header_field(out, ff_vma, padding);
170
// the field repeated for each profile class
171
for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
172
if (flags & ff_nr_samples)
173
padding = output_header_field(out,
174
ff_nr_samples, padding);
176
if (flags & ff_nr_samples_cumulated)
177
padding = output_header_field(out,
178
ff_nr_samples_cumulated, padding);
180
if (flags & ff_percent)
181
padding = output_header_field(out,
182
ff_percent, padding);
184
if (flags & ff_percent_cumulated)
185
padding = output_header_field(out,
186
ff_percent_cumulated, padding);
189
padding = output_header_field(out,
192
if (flags & ff_percent_details)
193
padding = output_header_field(out,
194
ff_percent_details, padding);
196
if (flags & ff_percent_cumulated_details)
197
padding = output_header_field(out,
198
ff_percent_cumulated_details, padding);
201
// now the remaining field
202
if (flags & ff_linenr_info)
203
padding = output_header_field(out, ff_linenr_info, padding);
205
if (flags & ff_image_name)
206
padding = output_header_field(out, ff_image_name, padding);
208
if (flags & ff_app_name)
209
padding = output_header_field(out, ff_app_name, padding);
211
if (flags & ff_symb_name)
212
padding = output_header_field(out, ff_symb_name, padding);
218
/// describe each possible field of colummned output.
219
// FIXME: use % of the screen width here. sum of % equal to 100, then calculate
220
// ratio between 100 and the selected % to grow non fixed field use also
221
// lib[n?]curses to get the console width (look info source) (so on add a fixed
224
output_field(ostream & out, field_datum const & datum,
225
format_flags fl, size_t padding, bool hide_immutable)
227
if (!hide_immutable) {
228
out << string(padding, ' ');
230
field_description const & field(format_map[fl]);
231
string str = (this->*field.formatter)(datum);
234
// at least one separator char
236
if (str.length() < field.width)
237
padding = field.width - str.length();
239
field_description const & field(format_map[fl]);
240
padding += field.width;
248
output_header_field(ostream & out, format_flags fl, size_t padding)
250
out << string(padding, ' ');
252
field_description const & field(format_map[fl]);
253
out << field.header_name;
255
// at least one separator char
257
if (field.header_name.length() < field.width)
258
padding = field.width - field.header_name.length();
264
string formatter::format_vma(field_datum const & f)
266
return get_vma(f.sample.vma, vma_64);
270
string formatter::format_symb_name(field_datum const & f)
272
return symbol_names.demangle(f.symbol.name);
276
string formatter::format_image_name(field_datum const & f)
278
return get_image_name(f.symbol.image_name,
280
? image_name_storage::int_real_filename
281
: image_name_storage::int_real_basename,
286
string formatter::format_app_name(field_datum const & f)
288
return get_image_name(f.symbol.app_name,
290
? image_name_storage::int_real_filename
291
: image_name_storage::int_real_basename,
296
string formatter::format_linenr_info(field_datum const & f)
298
return get_linenr_info(f.sample.file_loc, long_filenames);
302
string formatter::format_nr_samples(field_datum const & f)
305
out << f.sample.counts[f.pclass];
310
string formatter::format_nr_cumulated_samples(field_datum const & f)
312
if (f.diff == -INFINITY)
315
f.counts.cumulated_samples[f.pclass] += f.sample.counts[f.pclass];
316
out << f.counts.cumulated_samples[f.pclass];
321
string formatter::format_percent(field_datum const & f)
323
if (f.diff == -INFINITY)
325
return get_percent(f.sample.counts[f.pclass], f.counts.total[f.pclass]);
329
string formatter::format_cumulated_percent(field_datum const & f)
331
if (f.diff == -INFINITY)
333
f.counts.cumulated_percent[f.pclass] += f.sample.counts[f.pclass];
335
return get_percent(f.counts.cumulated_percent[f.pclass],
336
f.counts.total[f.pclass]);
340
string formatter::format_percent_details(field_datum const & f)
342
return get_percent(f.sample.counts[f.pclass],
343
f.counts.total[f.pclass]);
347
string formatter::format_cumulated_percent_details(field_datum const & f)
349
f.counts.cumulated_percent_details[f.pclass] += f.sample.counts[f.pclass];
351
return get_percent(f.counts.cumulated_percent_details[f.pclass],
352
f.counts.total[f.pclass]);
356
string formatter::format_diff(field_datum const & f)
358
if (f.diff == INFINITY)
360
else if (f.diff == -INFINITY)
363
return ::format_percent(f.diff, percent_int_width,
364
percent_fract_width, true);
369
do_output(ostream & out, symbol_entry const & symb, sample_entry const & sample,
370
counts_t & c, diff_array_t const & diffs, bool hide_immutable)
374
// first output the vma field
375
field_datum datum(symb, sample, 0, c, extra_found_images);
377
padding = output_field(out, datum, ff_vma, padding, false);
379
// repeated fields for each profile class
380
for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
381
field_datum datum(symb, sample, pclass, c,
382
extra_found_images, diffs[pclass]);
384
if (flags & ff_nr_samples)
385
padding = output_field(out, datum,
386
ff_nr_samples, padding, false);
388
if (flags & ff_nr_samples_cumulated)
389
padding = output_field(out, datum,
390
ff_nr_samples_cumulated, padding, false);
392
if (flags & ff_percent)
393
padding = output_field(out, datum,
394
ff_percent, padding, false);
396
if (flags & ff_percent_cumulated)
397
padding = output_field(out, datum,
398
ff_percent_cumulated, padding, false);
401
padding = output_field(out, datum,
402
ff_diff, padding, false);
404
if (flags & ff_percent_details)
405
padding = output_field(out, datum,
406
ff_percent_details, padding, false);
408
if (flags & ff_percent_cumulated_details)
409
padding = output_field(out, datum,
410
ff_percent_cumulated_details, padding, false);
413
// now the remaining field
414
if (flags & ff_linenr_info)
415
padding = output_field(out, datum, ff_linenr_info,
418
if (flags & ff_image_name)
419
padding = output_field(out, datum, ff_image_name,
420
padding, hide_immutable);
422
if (flags & ff_app_name)
423
padding = output_field(out, datum, ff_app_name,
424
padding, hide_immutable);
426
if (flags & ff_symb_name)
427
padding = output_field(out, datum, ff_symb_name,
428
padding, hide_immutable);
434
opreport_formatter::opreport_formatter(profile_container const & p)
436
formatter(p.extra_found_images),
440
counts.total = profile.samples_count();
444
void opreport_formatter::show_details(bool on_off)
446
need_details = on_off;
450
void opreport_formatter::output(ostream & out, symbol_entry const * symb)
452
do_output(out, *symb, symb->sample, counts);
455
output_details(out, symb);
459
void opreport_formatter::
460
output(ostream & out, symbol_collection const & syms)
464
symbol_collection::const_iterator it = syms.begin();
465
symbol_collection::const_iterator end = syms.end();
466
for (; it != end; ++it)
471
void opreport_formatter::
472
output_details(ostream & out, symbol_entry const * symb)
477
c.total = symb->sample.counts;
479
// cumulated percent are relative to current symbol.
480
c.cumulated_samples = count_array_t();
481
c.cumulated_percent = count_array_t();
483
sample_container::samples_iterator it = profile.begin(symb);
484
sample_container::samples_iterator end = profile.end(symb);
485
for (; it != end; ++it) {
487
do_output(out, *symb, it->second, c, diff_array_t(), true);
492
cg_formatter::cg_formatter(callgraph_container const & profile)
494
formatter(profile.extra_found_images)
496
counts.total = profile.samples_count();
500
void cg_formatter::output(ostream & out, symbol_collection const & syms)
502
// amount of spacing prefixing child and parent lines
503
string const child_parent_prefix(" ");
507
out << string(79, '-') << endl;
509
symbol_collection::const_iterator it;
510
symbol_collection::const_iterator end = syms.end();
512
for (it = syms.begin(); it < end; ++it) {
513
cg_symbol const * sym = dynamic_cast<cg_symbol const *>(*it);
515
cg_symbol::children::const_iterator cit;
516
cg_symbol::children::const_iterator cend = sym->callers.end();
520
c.total = counts.total;
522
c.total = sym->total_caller_count;
524
for (cit = sym->callers.begin(); cit != cend; ++cit) {
525
out << child_parent_prefix;
526
do_output(out, *cit, cit->sample, c);
529
do_output(out, *sym, sym->sample, counts);
533
c.total = counts.total;
535
c.total = sym->total_callee_count;
537
cend = sym->callees.end();
539
for (cit = sym->callees.begin(); cit != cend; ++cit) {
540
out << child_parent_prefix;
541
do_output(out, *cit, cit->sample, c);
544
out << string(79, '-') << endl;
549
diff_formatter::diff_formatter(diff_container const & profile,
550
extra_images const & extra)
554
counts.total = profile.samples_count();
558
void diff_formatter::output(ostream & out, diff_collection const & syms)
562
diff_collection::const_iterator it = syms.begin();
563
diff_collection::const_iterator end = syms.end();
564
for (; it != end; ++it)
565
do_output(out, *it, it->sample, counts, it->diffs);
568
// local variables used in generation of XML
569
// buffer details for output later
570
ostringstream bytes_out;
572
// module+symbol table for detecting duplicate symbols
573
map<string, size_t> symbol_data_table;
574
size_t symbol_data_index = 0;
576
/* Return any existing index or add to the table */
577
size_t xml_get_symbol_index(string const & name)
579
size_t index = symbol_data_index;
580
map<string, size_t>::iterator it = symbol_data_table.find(name);
582
if (it == symbol_data_table.end()) {
583
symbol_data_table[name] = symbol_data_index++;
591
class symbol_details_t {
593
symbol_details_t() { size = index = 0; id = -1; }
600
typedef growable_vector<symbol_details_t> symbol_details_array_t;
601
symbol_details_array_t symbol_details;
602
size_t detail_table_index = 0;
605
xml_formatter(profile_container const * p,
606
symbol_collection & s, extra_images const & extra,
607
string_filter const & sf)
616
counts.total = profile->samples_count();
621
show_details(bool on_off)
623
need_details = on_off;
627
void xml_formatter::output(ostream & out)
629
xml_support->build_subclasses(out);
631
xml_support->output_program_structure(out);
632
output_symbol_data(out);
634
out << open_element(DETAIL_TABLE);
635
for (size_t i = 0; i < symbol_details.size(); ++i) {
636
int id = symbol_details[i].id;
639
out << open_element(SYMBOL_DETAILS, true);
640
out << init_attr(TABLE_ID, (size_t)id);
641
out << close_element(NONE, true);
642
out << symbol_details[i].details;
643
out << close_element(SYMBOL_DETAILS);
646
out << close_element(DETAIL_TABLE);
649
out << open_element(BYTES_TABLE);
650
out << bytes_out.str();
651
out << close_element(BYTES_TABLE);
654
out << close_element(PROFILE);
658
xml_formatter::get_bfd_object(symbol_entry const * symb, op_bfd * & abfd) const
662
string const & image_name = get_image_name(symb->image_name,
663
image_name_storage::int_filename, extra_found_images);
664
if (symb->spu_offset) {
665
// FIXME: what about archive:tmp, actually it's not supported
666
// for spu since oparchive doesn't archive the real file but
667
// in future it would work ?
668
string tmp = get_image_name(symb->embedding_filename,
669
image_name_storage::int_filename, extra_found_images);
670
if (abfd && abfd->get_filename() == tmp)
673
abfd = new op_bfd(symb->spu_offset, tmp,
674
symbol_filter, extra_found_images, ok);
676
if (abfd && abfd->get_filename() == image_name)
679
abfd = new op_bfd(image_name, symbol_filter,
680
extra_found_images, ok);
685
report_image_error(image_name, image_format_failure,
686
false, extra_found_images);
696
output_the_symbol_data(ostream & out, symbol_entry const * symb, op_bfd * & abfd)
698
string const name = symbol_names.name(symb->name);
699
assert(name.size() > 0);
701
string const image = get_image_name(symb->image_name,
702
image_name_storage::int_filename, extra_found_images);
703
string const qname = image + ":" + name;
704
map<string, size_t>::iterator sd_it = symbol_data_table.find(qname);
706
if (sd_it != symbol_data_table.end()) {
707
// first time we've seen this symbol
708
out << open_element(SYMBOL_DATA, true);
709
out << init_attr(TABLE_ID, sd_it->second);
711
field_datum datum(*symb, symb->sample, 0, counts,
714
output_attribute(out, datum, ff_symb_name, NAME);
716
if (flags & ff_linenr_info) {
717
output_attribute(out, datum, ff_linenr_info, SOURCE_FILE);
718
output_attribute(out, datum, ff_linenr_info, SOURCE_LINE);
721
if (name.size() > 0 && name[0] != '?') {
722
output_attribute(out, datum, ff_vma, STARTING_ADDR);
725
get_bfd_object(symb, abfd);
726
if (abfd && abfd->symbol_has_contents(symb->sym_index))
727
xml_support->output_symbol_bytes(bytes_out, symb, sd_it->second, *abfd);
730
out << close_element();
732
// seen so remove (otherwise get several "no symbols")
733
symbol_data_table.erase(qname);
737
void xml_formatter::output_cg_children(ostream & out,
738
cg_symbol::children const cg_symb, op_bfd * & abfd)
740
cg_symbol::children::const_iterator cit;
741
cg_symbol::children::const_iterator cend = cg_symb.end();
743
for (cit = cg_symb.begin(); cit != cend; ++cit) {
744
string const name = symbol_names.name(cit->name);
745
string const image = get_image_name(cit->image_name,
746
image_name_storage::int_filename, extra_found_images);
747
string const qname = image + ":" + name;
748
map<string, size_t>::iterator sd_it = symbol_data_table.find(qname);
750
if (sd_it != symbol_data_table.end()) {
751
symbol_entry const * child = &(*cit);
752
output_the_symbol_data(out, child, abfd);
757
void xml_formatter::output_symbol_data(ostream & out)
759
op_bfd * abfd = NULL;
760
sym_iterator it = symbols.begin();
761
sym_iterator end = symbols.end();
763
out << open_element(SYMBOL_TABLE);
764
for ( ; it != end; ++it) {
765
symbol_entry const * symb = *it;
766
cg_symbol const * cg_symb = dynamic_cast<cg_symbol const *>(symb);
767
output_the_symbol_data(out, symb, abfd);
769
/* make sure callers/callees are included in SYMBOL_TABLE */
770
output_cg_children(out, cg_symb->callers, abfd);
771
output_cg_children(out, cg_symb->callees, abfd);
774
out << close_element(SYMBOL_TABLE);
779
string xml_formatter::
780
output_symbol_details(symbol_entry const * symb,
781
size_t & detail_index, size_t const lo, size_t const hi)
783
if (!has_sample_counts(symb->sample.counts, lo, hi))
786
sample_container::samples_iterator it = profile->begin(symb);
787
sample_container::samples_iterator end = profile->end(symb);
790
for (; it != end; ++it) {
793
for (size_t p = lo; p <= hi; ++p) {
794
size_t count = it->second.counts[p];
796
if (count == 0) continue;
798
str << open_element(DETAIL_DATA, true);
799
str << init_attr(TABLE_ID, detail_index++);
801
// first output the vma field
802
field_datum datum(*symb, it->second, 0, c,
803
extra_found_images, 0.0);
804
output_attribute(str, datum, ff_vma, VMA);
805
if (ff_linenr_info) {
810
string sym_info = get_linenr_info(symb->sample.file_loc, true);
811
string samp_info = get_linenr_info(it->second.file_loc, true);
813
if (extract_linenr_info(samp_info, samp_file, samp_line)) {
814
if (extract_linenr_info(sym_info, sym_file, sym_line)) {
815
// only output source_file if it is different than the symbol's
816
// source file. this can happen with inlined functions in
817
// #included header files
818
if (sym_file != samp_file)
819
str << init_attr(SOURCE_FILE, samp_file);
821
str << init_attr(SOURCE_LINE, samp_line);
824
str << close_element(NONE, true);
826
// output buffered sample data
827
output_sample_data(str, it->second, p);
829
str << close_element(DETAIL_DATA);
836
output_symbol(ostream & out,
837
symbol_entry const * symb, size_t lo, size_t hi, bool is_module)
840
// pointless reference to is_module, remove insane compiler warning
841
size_t indx = is_module ? 0 : 1;
843
// output symbol's summary data for each profile class
844
bool got_samples = false;
846
for (size_t p = lo; p <= hi; ++p) {
847
got_samples |= xml_support->output_summary_data(str,
848
symb->sample.counts, p);
855
out << "<!-- symbol_ref=" << symbol_names.name(symb->name) <<
858
out << open_element(SYMBOL, true);
860
string const name = symbol_names.name(symb->name);
861
assert(name.size() > 0);
863
string const image = get_image_name(symb->image_name,
864
image_name_storage::int_filename, extra_found_images);
865
string const qname = image + ":" + name;
867
indx = xml_get_symbol_index(qname);
869
out << init_attr(ID_REF, indx);
872
ostringstream details;
873
symbol_details_t & sd = symbol_details[indx];
874
size_t const detail_lo = sd.index;
876
string detail_str = output_symbol_details(symb, sd.index, lo, hi);
878
if (detail_str.size() > 0) {
881
details << detail_str;
884
if (sd.index > detail_lo) {
885
sd.details = sd.details + details.str();
886
out << init_attr(DETAIL_LO, detail_lo);
887
out << init_attr(DETAIL_HI, sd.index-1);
890
out << close_element(NONE, true);
893
out << close_element(SYMBOL);
898
output_sample_data(ostream & out, sample_entry const & sample, size_t pclass)
900
out << open_element(COUNT, true);
901
out << init_attr(CLASS, classes.v[pclass].name);
902
out << close_element(NONE, true);
903
out << sample.counts[pclass];
904
out << close_element(COUNT);
909
output_attribute(ostream & out, field_datum const & datum,
910
format_flags fl, tag_t tag)
912
field_description const & field(format_map[fl]);
914
string str = (this->*field.formatter)(datum);
917
if (fl == ff_linenr_info && (tag == SOURCE_LINE || tag == SOURCE_FILE)) {
921
if (extract_linenr_info(str, file, line)) {
922
if (tag == SOURCE_LINE)
923
out << init_attr(tag, line);
925
out << init_attr(tag, file);
928
out << " " << init_attr(tag, str);
933
xml_cg_formatter(callgraph_container const & cg, symbol_collection & s,
934
string_filter const & sf)
936
xml_formatter(0, s, cg.extra_found_images, sf),
939
counts.total = callgraph.samples_count();
942
void xml_cg_formatter::
943
output_symbol_core(ostream & out, cg_symbol::children const cg_symb,
944
string const selfname, string const qname,
945
size_t lo, size_t hi, bool is_module, tag_t tag)
947
cg_symbol::children::const_iterator cit;
948
cg_symbol::children::const_iterator cend = cg_symb.end();
950
for (cit = cg_symb.begin(); cit != cend; ++cit) {
951
string const & module = get_image_name((cit)->image_name,
952
image_name_storage::int_filename, extra_found_images);
957
// output symbol's summary data for each profile class
958
for (size_t p = lo; p <= hi; ++p)
959
xml_support->output_summary_data(str, cit->sample.counts, p);
962
out << "<!-- symbol_ref=" << symbol_names.name(cit->name) <<
966
out << open_element(MODULE, true);
967
out << init_attr(NAME, module) << close_element(NONE, true);
970
out << open_element(SYMBOL, true);
972
string const symname = symbol_names.name(cit->name);
973
assert(symname.size() > 0);
975
string const symqname = module + ":" + symname;
977
// Find any self references and handle
978
if ((symname == selfname) && (tag == CALLEES)) {
980
indx = xml_get_symbol_index(qname);
982
indx = xml_get_symbol_index(symqname);
985
out << init_attr(ID_REF, indx);
988
out << init_attr(SELFREF, "true");
990
out << close_element(NONE, true);
992
out << close_element(SYMBOL);
995
out << close_element(MODULE);
1000
void xml_cg_formatter::
1001
output_symbol(ostream & out,
1002
symbol_entry const * symb, size_t lo, size_t hi, bool is_module)
1004
cg_symbol const * cg_symb = dynamic_cast<cg_symbol const *>(symb);
1008
// output symbol's summary data for each profile class
1009
for (size_t p = lo; p <= hi; ++p)
1010
xml_support->output_summary_data(str, symb->sample.counts, p);
1013
out << "<!-- symbol_ref=" << symbol_names.name(symb->name) <<
1016
out << open_element(SYMBOL, true);
1018
string const name = symbol_names.name(symb->name);
1019
assert(name.size() > 0);
1021
string const image = get_image_name(symb->image_name,
1022
image_name_storage::int_filename, extra_found_images);
1023
string const qname = image + ":" + name;
1025
string const selfname = symbol_names.demangle(symb->name) + " [self]";
1027
indx = xml_get_symbol_index(qname);
1029
out << init_attr(ID_REF, indx);
1031
out << close_element(NONE, true);
1033
out << open_element(CALLERS);
1035
output_symbol_core(out, cg_symb->callers, selfname, qname, lo, hi, is_module, CALLERS);
1036
out << close_element(CALLERS);
1038
out << open_element(CALLEES);
1040
output_symbol_core(out, cg_symb->callees, selfname, qname, lo, hi, is_module, CALLEES);
1042
out << close_element(CALLEES);
1046
out << close_element(SYMBOL);
1049
} // namespace format_output