Home | History | Annotate | Download | only in libpp
      1 /**
      2  * @file format_output.cpp
      3  * outputting format for symbol lists
      4  *
      5  * @remark Copyright 2002 OProfile authors
      6  * @remark Read the file COPYING
      7  *
      8  * @author Philippe Elie
      9  * @author John Levon
     10  */
     11 
     12 /* older glibc has C99 INFINITY in _GNU_SOURCE */
     13 #ifndef _GNU_SOURCE
     14 #define _GNU_SOURCE
     15 #endif
     16 
     17 #include <cassert>
     18 #include <sstream>
     19 #include <iomanip>
     20 #include <iostream>
     21 #include <cmath>
     22 
     23 #include "string_manip.h"
     24 #include "string_filter.h"
     25 
     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"
     33 #include "cverb.h"
     34 
     35 using namespace std;
     36 
     37 namespace {
     38 
     39 
     40 string const get_linenr_info(file_location const floc, bool lf)
     41 {
     42 	ostringstream out;
     43 
     44 	string const & filename = lf
     45 		? debug_names.name(floc.filename)
     46 		: debug_names.basename(floc.filename);
     47 
     48 	if (!filename.empty())
     49 		out << filename << ":" << floc.linenr;
     50 	else
     51 		out << "(no location information)";
     52 
     53 	return out.str();
     54 }
     55 
     56 string get_vma(bfd_vma vma, bool vma_64)
     57 {
     58 	ostringstream out;
     59 	int width = vma_64 ? 16 : 8;
     60 
     61 	out << hex << setw(width) << setfill('0') << vma;
     62 
     63 	return out.str();
     64 }
     65 
     66 string get_percent(count_type dividend, count_type divisor)
     67 {
     68 	double ratio = op_ratio(dividend, divisor);
     69 
     70 	return ::format_percent(ratio * 100, percent_int_width,
     71 	                     percent_fract_width);
     72 }
     73 
     74 bool extract_linenr_info(string const & info, string & file, size_t & line)
     75 {
     76 	line = 0;
     77 	file = "";
     78 	string::size_type colon_pos = info.find(":");
     79 
     80 	if (colon_pos == string::npos)
     81 		return false;
     82 
     83 	file = info.substr(0, colon_pos);
     84 	istringstream is_info(info.substr(colon_pos+1));
     85 	is_info >> line;
     86 	return true;
     87 }
     88 
     89 
     90 } // anonymous namespace
     91 
     92 namespace format_output {
     93 
     94 formatter::formatter(extra_images const & extra)
     95 	:
     96 	nr_classes(1),
     97 	flags(ff_none),
     98 	vma_64(false),
     99 	long_filenames(false),
    100 	need_header(true),
    101 	extra_found_images(extra)
    102 {
    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);
    115 }
    116 
    117 
    118 formatter::~formatter()
    119 {
    120 }
    121 
    122 
    123 void formatter::set_nr_classes(size_t nr)
    124 {
    125 	nr_classes = nr;
    126 }
    127 
    128 
    129 void formatter::add_format(format_flags flag)
    130 {
    131 	flags = static_cast<format_flags>(flags | flag);
    132 }
    133 
    134 
    135 void formatter::show_header(bool on_off)
    136 {
    137 	need_header = on_off;
    138 }
    139 
    140 
    141 void formatter::vma_format_64bit(bool on_off)
    142 {
    143 	vma_64 = on_off;
    144 }
    145 
    146 
    147 void formatter::show_long_filenames(bool on_off)
    148 {
    149 	long_filenames = on_off;
    150 }
    151 
    152 
    153 void formatter::show_global_percent(bool on_off)
    154 {
    155 	global_percent = on_off;
    156 }
    157 
    158 
    159 void formatter::output_header(ostream & out)
    160 {
    161 	if (!need_header)
    162 		return;
    163 
    164 	size_t padding = 0;
    165 
    166 	// first output the vma field
    167 	if (flags & ff_vma)
    168 		padding = output_header_field(out, ff_vma, padding);
    169 
    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);
    175 
    176 		if (flags & ff_nr_samples_cumulated)
    177 			padding = output_header_field(out,
    178 			       ff_nr_samples_cumulated, padding);
    179 
    180 		if (flags & ff_percent)
    181 			padding = output_header_field(out,
    182 			       ff_percent, padding);
    183 
    184 		if (flags & ff_percent_cumulated)
    185 			padding = output_header_field(out,
    186 			       ff_percent_cumulated, padding);
    187 
    188 		if (flags & ff_diff)
    189 			padding = output_header_field(out,
    190 				ff_diff, padding);
    191 
    192 		if (flags & ff_percent_details)
    193 			padding = output_header_field(out,
    194 			       ff_percent_details, padding);
    195 
    196 		if (flags & ff_percent_cumulated_details)
    197 			padding = output_header_field(out,
    198 			       ff_percent_cumulated_details, padding);
    199 	}
    200 
    201 	// now the remaining field
    202 	if (flags & ff_linenr_info)
    203 		padding = output_header_field(out, ff_linenr_info, padding);
    204 
    205 	if (flags & ff_image_name)
    206 		padding = output_header_field(out, ff_image_name, padding);
    207 
    208 	if (flags & ff_app_name)
    209 		padding = output_header_field(out, ff_app_name, padding);
    210 
    211 	if (flags & ff_symb_name)
    212 		padding = output_header_field(out, ff_symb_name, padding);
    213 
    214 	out << "\n";
    215 }
    216 
    217 
    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
    222 // field flags)
    223 size_t formatter::
    224 output_field(ostream & out, field_datum const & datum,
    225              format_flags fl, size_t padding, bool hide_immutable)
    226 {
    227 	if (!hide_immutable) {
    228 		out << string(padding, ' ');
    229 
    230 		field_description const & field(format_map[fl]);
    231 		string str = (this->*field.formatter)(datum);
    232 		out << str;
    233 
    234 		// at least one separator char
    235 		padding = 1;
    236 		if (str.length() < field.width)
    237 			padding = field.width - str.length();
    238 	} else {
    239 		field_description const & field(format_map[fl]);
    240 		padding += field.width;
    241 	}
    242 
    243 	return padding;
    244 }
    245 
    246 
    247 size_t formatter::
    248 output_header_field(ostream & out, format_flags fl, size_t padding)
    249 {
    250 	out << string(padding, ' ');
    251 
    252 	field_description const & field(format_map[fl]);
    253 	out << field.header_name;
    254 
    255 	// at least one separator char
    256 	padding = 1;
    257 	if (field.header_name.length() < field.width)
    258 		padding = field.width - field.header_name.length();
    259 
    260 	return padding;
    261 }
    262 
    263 
    264 string formatter::format_vma(field_datum const & f)
    265 {
    266 	return get_vma(f.sample.vma, vma_64);
    267 }
    268 
    269 
    270 string formatter::format_symb_name(field_datum const & f)
    271 {
    272 	return symbol_names.demangle(f.symbol.name);
    273 }
    274 
    275 
    276 string formatter::format_image_name(field_datum const & f)
    277 {
    278 	return get_image_name(f.symbol.image_name,
    279 		long_filenames
    280 			? image_name_storage::int_real_filename
    281 			: image_name_storage::int_real_basename,
    282 		extra_found_images);
    283 }
    284 
    285 
    286 string formatter::format_app_name(field_datum const & f)
    287 {
    288 	return get_image_name(f.symbol.app_name,
    289 		long_filenames
    290 			? image_name_storage::int_real_filename
    291 			: image_name_storage::int_real_basename,
    292 		extra_found_images);
    293 }
    294 
    295 
    296 string formatter::format_linenr_info(field_datum const & f)
    297 {
    298 	return get_linenr_info(f.sample.file_loc, long_filenames);
    299 }
    300 
    301 
    302 string formatter::format_nr_samples(field_datum const & f)
    303 {
    304 	ostringstream out;
    305 	out << f.sample.counts[f.pclass];
    306 	return out.str();
    307 }
    308 
    309 
    310 string formatter::format_nr_cumulated_samples(field_datum const & f)
    311 {
    312 	if (f.diff == -INFINITY)
    313 		return "---";
    314 	ostringstream out;
    315 	f.counts.cumulated_samples[f.pclass] += f.sample.counts[f.pclass];
    316 	out << f.counts.cumulated_samples[f.pclass];
    317 	return out.str();
    318 }
    319 
    320 
    321 string formatter::format_percent(field_datum const & f)
    322 {
    323 	if (f.diff == -INFINITY)
    324 		return "---";
    325 	return get_percent(f.sample.counts[f.pclass], f.counts.total[f.pclass]);
    326 }
    327 
    328 
    329 string formatter::format_cumulated_percent(field_datum const & f)
    330 {
    331 	if (f.diff == -INFINITY)
    332 		return "---";
    333 	f.counts.cumulated_percent[f.pclass] += f.sample.counts[f.pclass];
    334 
    335 	return get_percent(f.counts.cumulated_percent[f.pclass],
    336 	                   f.counts.total[f.pclass]);
    337 }
    338 
    339 
    340 string formatter::format_percent_details(field_datum const & f)
    341 {
    342 	return get_percent(f.sample.counts[f.pclass],
    343 		f.counts.total[f.pclass]);
    344 }
    345 
    346 
    347 string formatter::format_cumulated_percent_details(field_datum const & f)
    348 {
    349 	f.counts.cumulated_percent_details[f.pclass] += f.sample.counts[f.pclass];
    350 
    351 	return get_percent(f.counts.cumulated_percent_details[f.pclass],
    352 	                   f.counts.total[f.pclass]);
    353 }
    354 
    355 
    356 string formatter::format_diff(field_datum const & f)
    357 {
    358 	if (f.diff == INFINITY)
    359 		return "+++";
    360 	else if (f.diff == -INFINITY)
    361 		return "---";
    362 
    363 	return ::format_percent(f.diff, percent_int_width,
    364                                 percent_fract_width, true);
    365 }
    366 
    367 
    368 void formatter::
    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)
    371 {
    372 	size_t padding = 0;
    373 
    374 	// first output the vma field
    375 	field_datum datum(symb, sample, 0, c, extra_found_images);
    376 	if (flags & ff_vma)
    377 		padding = output_field(out, datum, ff_vma, padding, false);
    378 
    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]);
    383 
    384 		if (flags & ff_nr_samples)
    385 			padding = output_field(out, datum,
    386 			       ff_nr_samples, padding, false);
    387 
    388 		if (flags & ff_nr_samples_cumulated)
    389 			padding = output_field(out, datum,
    390 			       ff_nr_samples_cumulated, padding, false);
    391 
    392 		if (flags & ff_percent)
    393 			padding = output_field(out, datum,
    394 			       ff_percent, padding, false);
    395 
    396 		if (flags & ff_percent_cumulated)
    397 			padding = output_field(out, datum,
    398 			       ff_percent_cumulated, padding, false);
    399 
    400 		if (flags & ff_diff)
    401 			padding = output_field(out, datum,
    402 				ff_diff, padding, false);
    403 
    404 		if (flags & ff_percent_details)
    405 			padding = output_field(out, datum,
    406 			       ff_percent_details, padding, false);
    407 
    408 		if (flags & ff_percent_cumulated_details)
    409 			padding = output_field(out, datum,
    410 			       ff_percent_cumulated_details, padding, false);
    411 	}
    412 
    413 	// now the remaining field
    414 	if (flags & ff_linenr_info)
    415 		padding = output_field(out, datum, ff_linenr_info,
    416 		       padding, false);
    417 
    418 	if (flags & ff_image_name)
    419 		padding = output_field(out, datum, ff_image_name,
    420 		       padding, hide_immutable);
    421 
    422 	if (flags & ff_app_name)
    423 		padding = output_field(out, datum, ff_app_name,
    424 		       padding, hide_immutable);
    425 
    426 	if (flags & ff_symb_name)
    427 		padding = output_field(out, datum, ff_symb_name,
    428 		       padding, hide_immutable);
    429 
    430 	out << "\n";
    431 }
    432 
    433 
    434 opreport_formatter::opreport_formatter(profile_container const & p)
    435 	:
    436 	formatter(p.extra_found_images),
    437 	profile(p),
    438 	need_details(false)
    439 {
    440 	counts.total = profile.samples_count();
    441 }
    442 
    443 
    444 void opreport_formatter::show_details(bool on_off)
    445 {
    446 	need_details = on_off;
    447 }
    448 
    449 
    450 void opreport_formatter::output(ostream & out, symbol_entry const * symb)
    451 {
    452 	do_output(out, *symb, symb->sample, counts);
    453 
    454 	if (need_details)
    455 		output_details(out, symb);
    456 }
    457 
    458 
    459 void opreport_formatter::
    460 output(ostream & out, symbol_collection const & syms)
    461 {
    462 	output_header(out);
    463 
    464 	symbol_collection::const_iterator it = syms.begin();
    465 	symbol_collection::const_iterator end = syms.end();
    466 	for (; it != end; ++it)
    467 		output(out, *it);
    468 }
    469 
    470 
    471 void opreport_formatter::
    472 output_details(ostream & out, symbol_entry const * symb)
    473 {
    474 	counts_t c = counts;
    475 
    476 	if (!global_percent)
    477 		c.total = symb->sample.counts;
    478 
    479 	// cumulated percent are relative to current symbol.
    480 	c.cumulated_samples = count_array_t();
    481 	c.cumulated_percent = count_array_t();
    482 
    483 	sample_container::samples_iterator it = profile.begin(symb);
    484 	sample_container::samples_iterator end = profile.end(symb);
    485 	for (; it != end; ++it) {
    486 		out << "  ";
    487 		do_output(out, *symb, it->second, c, diff_array_t(), true);
    488 	}
    489 }
    490 
    491 
    492 cg_formatter::cg_formatter(callgraph_container const & profile)
    493 	:
    494 	formatter(profile.extra_found_images)
    495 {
    496 	counts.total = profile.samples_count();
    497 }
    498 
    499 
    500 void cg_formatter::output(ostream & out, symbol_collection const & syms)
    501 {
    502 	// amount of spacing prefixing child and parent lines
    503 	string const child_parent_prefix("  ");
    504 
    505 	output_header(out);
    506 
    507 	out << string(79, '-') << endl;
    508 
    509 	symbol_collection::const_iterator it;
    510 	symbol_collection::const_iterator end = syms.end();
    511 
    512 	for (it = syms.begin(); it < end; ++it) {
    513 		cg_symbol const * sym = dynamic_cast<cg_symbol const *>(*it);
    514 
    515 		cg_symbol::children::const_iterator cit;
    516 		cg_symbol::children::const_iterator cend = sym->callers.end();
    517 
    518 		counts_t c;
    519 		if (global_percent)
    520 			c.total = counts.total;
    521 		else
    522 			c.total = sym->total_caller_count;
    523 
    524 		for (cit = sym->callers.begin(); cit != cend; ++cit) {
    525 			out << child_parent_prefix;
    526 			do_output(out, *cit, cit->sample, c);
    527 		}
    528 
    529 		do_output(out, *sym, sym->sample, counts);
    530 
    531 		c = counts_t();
    532 		if (global_percent)
    533 			c.total = counts.total;
    534 		else
    535 			c.total = sym->total_callee_count;
    536 
    537 		cend = sym->callees.end();
    538 
    539 		for (cit = sym->callees.begin(); cit != cend; ++cit) {
    540 			out << child_parent_prefix;
    541 			do_output(out, *cit, cit->sample, c);
    542 		}
    543 
    544 		out << string(79, '-') << endl;
    545 	}
    546 }
    547 
    548 
    549 diff_formatter::diff_formatter(diff_container const & profile,
    550 			       extra_images const & extra)
    551 	:
    552 	formatter(extra)
    553 {
    554 	counts.total = profile.samples_count();
    555 }
    556 
    557 
    558 void diff_formatter::output(ostream & out, diff_collection const & syms)
    559 {
    560 	output_header(out);
    561 
    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);
    566 }
    567 
    568 // local variables used in generation of XML
    569 // buffer details for output later
    570 ostringstream bytes_out;
    571 
    572 // module+symbol table for detecting duplicate symbols
    573 map<string, size_t> symbol_data_table;
    574 size_t symbol_data_index = 0;
    575 
    576 /* Return any existing index or add to the table */
    577 size_t xml_get_symbol_index(string const & name)
    578 {
    579 	size_t index = symbol_data_index;
    580 	map<string, size_t>::iterator it = symbol_data_table.find(name);
    581 
    582 	if (it == symbol_data_table.end()) {
    583 		symbol_data_table[name] = symbol_data_index++;
    584 		return index;
    585 	}
    586 
    587 	return it->second;
    588 }
    589 
    590 
    591 class symbol_details_t {
    592 public:
    593 	symbol_details_t() { size = index = 0; id = -1; }
    594 	int id;
    595 	size_t size;
    596 	size_t index;
    597 	string details;
    598 };
    599 
    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;
    603 
    604 xml_formatter::
    605 xml_formatter(profile_container const * p,
    606 	      symbol_collection & s, extra_images const & extra,
    607 	      string_filter const & sf)
    608 	:
    609 	formatter(extra),
    610 	profile(p),
    611 	symbols(s),
    612 	need_details(false),
    613 	symbol_filter(sf)
    614 {
    615 	if (profile)
    616 		counts.total = profile->samples_count();
    617 }
    618 
    619 
    620 void xml_formatter::
    621 show_details(bool on_off)
    622 {
    623 	need_details = on_off;
    624 }
    625 
    626 
    627 void xml_formatter::output(ostream & out)
    628 {
    629 	xml_support->build_subclasses(out);
    630 
    631 	xml_support->output_program_structure(out);
    632 	output_symbol_data(out);
    633 	if (need_details) {
    634 		out << open_element(DETAIL_TABLE);
    635 		for (size_t i = 0; i < symbol_details.size(); ++i) {
    636 			int id = symbol_details[i].id;
    637 
    638 			if (id >= 0) {
    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);
    644 			}
    645 		}
    646 		out << close_element(DETAIL_TABLE);
    647 
    648 		// output bytesTable
    649 		out << open_element(BYTES_TABLE);
    650 		out << bytes_out.str();
    651 		out << close_element(BYTES_TABLE);
    652 	}
    653 
    654 	out << close_element(PROFILE);
    655 }
    656 
    657 bool
    658 xml_formatter::get_bfd_object(symbol_entry const * symb, op_bfd * & abfd) const
    659 {
    660 	bool ok = true;
    661 
    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)
    671 			return true;
    672 		delete abfd;
    673 		abfd = new op_bfd(symb->spu_offset, tmp,
    674 				  symbol_filter, extra_found_images, ok);
    675 	} else {
    676 		if (abfd && abfd->get_filename() == image_name)
    677 			return true;
    678 		delete abfd;
    679 		abfd = new op_bfd(image_name, symbol_filter,
    680 				  extra_found_images, ok);
    681 
    682 	}
    683 
    684 	if (!ok) {
    685 		report_image_error(image_name, image_format_failure,
    686 				   false, extra_found_images);
    687 		delete abfd;
    688 		abfd = 0;
    689 		return false;
    690 	}
    691 
    692 	return true;
    693 }
    694 
    695 void xml_formatter::
    696 output_the_symbol_data(ostream & out, symbol_entry const * symb, op_bfd * & abfd)
    697 {
    698 	string const name = symbol_names.name(symb->name);
    699 	assert(name.size() > 0);
    700 
    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);
    705 
    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);
    710 
    711 		field_datum datum(*symb, symb->sample, 0, counts,
    712 				  extra_found_images);
    713 
    714 		output_attribute(out, datum, ff_symb_name, NAME);
    715 
    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);
    719 		}
    720 
    721 		if (name.size() > 0 && name[0] != '?') {
    722 			output_attribute(out, datum, ff_vma, STARTING_ADDR);
    723 
    724 			if (need_details) {
    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);
    728 			}
    729 		}
    730 		out << close_element();
    731 
    732 		// seen so remove (otherwise get several "no symbols")
    733 		symbol_data_table.erase(qname);
    734 	}
    735 }
    736 
    737 void xml_formatter::output_cg_children(ostream & out,
    738 	cg_symbol::children const cg_symb, op_bfd * & abfd)
    739 {
    740 	cg_symbol::children::const_iterator cit;
    741 	cg_symbol::children::const_iterator cend = cg_symb.end();
    742 
    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);
    749 
    750 		if (sd_it != symbol_data_table.end()) {
    751 			symbol_entry const * child = &(*cit);
    752 			output_the_symbol_data(out, child, abfd);
    753 		}
    754 	}
    755 }
    756 
    757 void xml_formatter::output_symbol_data(ostream & out)
    758 {
    759 	op_bfd * abfd = NULL;
    760 	sym_iterator it = symbols.begin();
    761 	sym_iterator end = symbols.end();
    762 
    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);
    768 		if (cg_symb) {
    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);
    772 		}
    773 	}
    774 	out << close_element(SYMBOL_TABLE);
    775 
    776 	delete abfd;
    777 }
    778 
    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)
    782 {
    783 	if (!has_sample_counts(symb->sample.counts, lo, hi))
    784 		return "";
    785 
    786 	sample_container::samples_iterator it = profile->begin(symb);
    787 	sample_container::samples_iterator end = profile->end(symb);
    788 
    789 	ostringstream str;
    790 	for (; it != end; ++it) {
    791 		counts_t c;
    792 
    793 		for (size_t p = lo; p <= hi; ++p)  {
    794 			size_t count = it->second.counts[p];
    795 
    796 			if (count == 0) continue;
    797 
    798 			str << open_element(DETAIL_DATA, true);
    799 			str << init_attr(TABLE_ID, detail_index++);
    800 
    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) {
    806 				string sym_file;
    807 				size_t sym_line;
    808 				string samp_file;
    809 				size_t samp_line;
    810 				string sym_info = get_linenr_info(symb->sample.file_loc, true);
    811 				string samp_info = get_linenr_info(it->second.file_loc, true);
    812 
    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);
    820 					}
    821 					str << init_attr(SOURCE_LINE, samp_line);
    822 				}
    823 			}
    824 			str << close_element(NONE, true);
    825 
    826 			// output buffered sample data
    827 			output_sample_data(str, it->second, p);
    828 
    829 			str << close_element(DETAIL_DATA);
    830 		}
    831 	}
    832 	return str.str();
    833 }
    834 
    835 void xml_formatter::
    836 output_symbol(ostream & out,
    837 	symbol_entry const * symb, size_t lo, size_t hi, bool is_module)
    838 {
    839 	ostringstream str;
    840 	// pointless reference to is_module, remove insane compiler warning
    841 	size_t indx = is_module ? 0 : 1;
    842 
    843 	// output symbol's summary data for each profile class
    844 	bool got_samples = false;
    845 
    846 	for (size_t p = lo; p <= hi; ++p) {
    847 		got_samples |= xml_support->output_summary_data(str,
    848 		    symb->sample.counts, p);
    849 	}
    850 
    851 	if (!got_samples)
    852 		return;
    853 
    854 	if (cverb << vxml)
    855 		out << "<!-- symbol_ref=" << symbol_names.name(symb->name) <<
    856 			" -->" << endl;
    857 
    858 	out << open_element(SYMBOL, true);
    859 
    860 	string const name = symbol_names.name(symb->name);
    861 	assert(name.size() > 0);
    862 
    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;
    866 
    867 	indx = xml_get_symbol_index(qname);
    868 
    869 	out << init_attr(ID_REF, indx);
    870 
    871 	if (need_details) {
    872 		ostringstream details;
    873 		symbol_details_t & sd = symbol_details[indx];
    874 		size_t const detail_lo = sd.index;
    875 
    876 		string detail_str = output_symbol_details(symb, sd.index, lo, hi);
    877 
    878 		if (detail_str.size() > 0) {
    879 			if (sd.id < 0)
    880 				sd.id = indx;
    881 			details << detail_str;
    882 		}
    883 
    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);
    888 		}
    889 	}
    890 	out << close_element(NONE, true);
    891 	// output summary
    892 	out << str.str();
    893 	out << close_element(SYMBOL);
    894 }
    895 
    896 
    897 void xml_formatter::
    898 output_sample_data(ostream & out, sample_entry const & sample, size_t pclass)
    899 {
    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);
    905 }
    906 
    907 
    908 void xml_formatter::
    909 output_attribute(ostream & out, field_datum const & datum,
    910                  format_flags fl, tag_t tag)
    911 {
    912 	field_description const & field(format_map[fl]);
    913 
    914 	string str = (this->*field.formatter)(datum);
    915 
    916 	if (!str.empty()) {
    917 		if (fl == ff_linenr_info && (tag == SOURCE_LINE || tag == SOURCE_FILE)) {
    918 			string file;
    919 			size_t line;
    920 
    921 			if (extract_linenr_info(str, file, line)) {
    922 				if (tag == SOURCE_LINE)
    923 					out << init_attr(tag, line);
    924 				else
    925 					out << init_attr(tag, file);
    926 			}
    927 		} else
    928 			out << " " << init_attr(tag, str);
    929 	}
    930 }
    931 
    932 xml_cg_formatter::
    933 xml_cg_formatter(callgraph_container const & cg, symbol_collection & s,
    934 		 string_filter const & sf)
    935 	:
    936 	xml_formatter(0, s, cg.extra_found_images, sf),
    937 	callgraph(cg)
    938 {
    939 	counts.total = callgraph.samples_count();
    940 }
    941 
    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)
    946 {
    947 	cg_symbol::children::const_iterator cit;
    948 	cg_symbol::children::const_iterator cend = cg_symb.end();
    949 
    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);
    953 		bool self = false;
    954 		ostringstream str;
    955 		size_t indx;
    956 
    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);
    960 
    961 		if (cverb << vxml)
    962 			out << "<!-- symbol_ref=" << symbol_names.name(cit->name) <<
    963 				" -->" << endl;
    964 
    965 		if (is_module) {
    966 			out << open_element(MODULE, true);
    967 			out << init_attr(NAME, module) << close_element(NONE, true);
    968 		}
    969 
    970 		out << open_element(SYMBOL, true);
    971 
    972 		string const symname = symbol_names.name(cit->name);
    973 		assert(symname.size() > 0);
    974 
    975 		string const symqname = module + ":" + symname;
    976 
    977 		// Find any self references and handle
    978 		if ((symname == selfname) && (tag == CALLEES)) {
    979 			self = true;
    980 			indx = xml_get_symbol_index(qname);
    981 		} else {
    982 			indx = xml_get_symbol_index(symqname);
    983 		}
    984 
    985 		out << init_attr(ID_REF, indx);
    986 
    987 		if (self)
    988 			out << init_attr(SELFREF, "true");
    989 
    990 		out << close_element(NONE, true);
    991 		out << str.str();
    992 		out << close_element(SYMBOL);
    993 
    994 		if (is_module)
    995 			out << close_element(MODULE);
    996 	}
    997 }
    998 
    999 
   1000 void xml_cg_formatter::
   1001 output_symbol(ostream & out,
   1002 	symbol_entry const * symb, size_t lo, size_t hi, bool is_module)
   1003 {
   1004 	cg_symbol const * cg_symb = dynamic_cast<cg_symbol const *>(symb);
   1005 	ostringstream str;
   1006 	size_t indx;
   1007 
   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);
   1011 
   1012 	if (cverb << vxml)
   1013 		out << "<!-- symbol_ref=" << symbol_names.name(symb->name) <<
   1014 			" -->" << endl;
   1015 
   1016 	out << open_element(SYMBOL, true);
   1017 
   1018 	string const name = symbol_names.name(symb->name);
   1019 	assert(name.size() > 0);
   1020 
   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;
   1024 
   1025 	string const selfname = symbol_names.demangle(symb->name) + " [self]";
   1026 
   1027 	indx = xml_get_symbol_index(qname);
   1028 
   1029 	out << init_attr(ID_REF, indx);
   1030 
   1031 	out << close_element(NONE, true);
   1032 
   1033 	out << open_element(CALLERS);
   1034 	if (cg_symb)
   1035 		output_symbol_core(out, cg_symb->callers, selfname, qname, lo, hi, is_module, CALLERS);
   1036 	out << close_element(CALLERS);
   1037 
   1038 	out << open_element(CALLEES);
   1039 	if (cg_symb)
   1040 		output_symbol_core(out, cg_symb->callees, selfname, qname, lo, hi, is_module, CALLEES);
   1041 
   1042 	out << close_element(CALLEES);
   1043 
   1044 	// output summary
   1045 	out << str.str();
   1046 	out << close_element(SYMBOL);
   1047 }
   1048 
   1049 } // namespace format_output
   1050