| /** |
| * @file format_output.cpp |
| * outputting format for symbol lists |
| * |
| * @remark Copyright 2002 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author Philippe Elie |
| * @author John Levon |
| */ |
| |
| /* older glibc has C99 INFINITY in _GNU_SOURCE */ |
| #ifndef _GNU_SOURCE |
| #define _GNU_SOURCE |
| #endif |
| |
| #include <cassert> |
| #include <sstream> |
| #include <iomanip> |
| #include <iostream> |
| #include <cmath> |
| |
| #include "string_manip.h" |
| #include "string_filter.h" |
| |
| #include "format_output.h" |
| #include "profile_container.h" |
| #include "callgraph_container.h" |
| #include "diff_container.h" |
| #include "arrange_profiles.h" |
| #include "xml_output.h" |
| #include "xml_utils.h" |
| #include "cverb.h" |
| |
| using namespace std; |
| |
| namespace { |
| |
| |
| string const get_linenr_info(file_location const floc, bool lf) |
| { |
| ostringstream out; |
| |
| string const & filename = lf |
| ? debug_names.name(floc.filename) |
| : debug_names.basename(floc.filename); |
| |
| if (!filename.empty()) |
| out << filename << ":" << floc.linenr; |
| else |
| out << "(no location information)"; |
| |
| return out.str(); |
| } |
| |
| string get_vma(bfd_vma vma, bool vma_64) |
| { |
| ostringstream out; |
| int width = vma_64 ? 16 : 8; |
| |
| out << hex << setw(width) << setfill('0') << vma; |
| |
| return out.str(); |
| } |
| |
| string get_percent(count_type dividend, count_type divisor) |
| { |
| double ratio = op_ratio(dividend, divisor); |
| |
| return ::format_percent(ratio * 100, percent_int_width, |
| percent_fract_width); |
| } |
| |
| bool extract_linenr_info(string const & info, string & file, size_t & line) |
| { |
| line = 0; |
| file = ""; |
| string::size_type colon_pos = info.find(":"); |
| |
| if (colon_pos == string::npos) |
| return false; |
| |
| file = info.substr(0, colon_pos); |
| istringstream is_info(info.substr(colon_pos+1)); |
| is_info >> line; |
| return true; |
| } |
| |
| |
| } // anonymous namespace |
| |
| namespace format_output { |
| |
| formatter::formatter(extra_images const & extra) |
| : |
| nr_classes(1), |
| flags(ff_none), |
| vma_64(false), |
| long_filenames(false), |
| need_header(true), |
| extra_found_images(extra) |
| { |
| format_map[ff_vma] = field_description(9, "vma", &formatter::format_vma); |
| format_map[ff_nr_samples] = field_description(9, "samples", &formatter::format_nr_samples); |
| format_map[ff_nr_samples_cumulated] = field_description(14, "cum. samples", &formatter::format_nr_cumulated_samples); |
| format_map[ff_percent] = field_description(9, "%", &formatter::format_percent); |
| format_map[ff_percent_cumulated] = field_description(11, "cum. %", &formatter::format_cumulated_percent); |
| format_map[ff_linenr_info] = field_description(28, "linenr info", &formatter::format_linenr_info); |
| format_map[ff_image_name] = field_description(25, "image name", &formatter::format_image_name); |
| format_map[ff_app_name] = field_description(25, "app name", &formatter::format_app_name); |
| format_map[ff_symb_name] = field_description(30, "symbol name", &formatter::format_symb_name); |
| format_map[ff_percent_details] = field_description(9, "%", &formatter::format_percent_details); |
| format_map[ff_percent_cumulated_details] = field_description(10, "cum. %", &formatter::format_cumulated_percent_details); |
| format_map[ff_diff] = field_description(10, "diff %", &formatter::format_diff); |
| } |
| |
| |
| formatter::~formatter() |
| { |
| } |
| |
| |
| void formatter::set_nr_classes(size_t nr) |
| { |
| nr_classes = nr; |
| } |
| |
| |
| void formatter::add_format(format_flags flag) |
| { |
| flags = static_cast<format_flags>(flags | flag); |
| } |
| |
| |
| void formatter::show_header(bool on_off) |
| { |
| need_header = on_off; |
| } |
| |
| |
| void formatter::vma_format_64bit(bool on_off) |
| { |
| vma_64 = on_off; |
| } |
| |
| |
| void formatter::show_long_filenames(bool on_off) |
| { |
| long_filenames = on_off; |
| } |
| |
| |
| void formatter::show_global_percent(bool on_off) |
| { |
| global_percent = on_off; |
| } |
| |
| |
| void formatter::output_header(ostream & out) |
| { |
| if (!need_header) |
| return; |
| |
| size_t padding = 0; |
| |
| // first output the vma field |
| if (flags & ff_vma) |
| padding = output_header_field(out, ff_vma, padding); |
| |
| // the field repeated for each profile class |
| for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) { |
| if (flags & ff_nr_samples) |
| padding = output_header_field(out, |
| ff_nr_samples, padding); |
| |
| if (flags & ff_nr_samples_cumulated) |
| padding = output_header_field(out, |
| ff_nr_samples_cumulated, padding); |
| |
| if (flags & ff_percent) |
| padding = output_header_field(out, |
| ff_percent, padding); |
| |
| if (flags & ff_percent_cumulated) |
| padding = output_header_field(out, |
| ff_percent_cumulated, padding); |
| |
| if (flags & ff_diff) |
| padding = output_header_field(out, |
| ff_diff, padding); |
| |
| if (flags & ff_percent_details) |
| padding = output_header_field(out, |
| ff_percent_details, padding); |
| |
| if (flags & ff_percent_cumulated_details) |
| padding = output_header_field(out, |
| ff_percent_cumulated_details, padding); |
| } |
| |
| // now the remaining field |
| if (flags & ff_linenr_info) |
| padding = output_header_field(out, ff_linenr_info, padding); |
| |
| if (flags & ff_image_name) |
| padding = output_header_field(out, ff_image_name, padding); |
| |
| if (flags & ff_app_name) |
| padding = output_header_field(out, ff_app_name, padding); |
| |
| if (flags & ff_symb_name) |
| padding = output_header_field(out, ff_symb_name, padding); |
| |
| out << "\n"; |
| } |
| |
| |
| /// describe each possible field of colummned output. |
| // FIXME: use % of the screen width here. sum of % equal to 100, then calculate |
| // ratio between 100 and the selected % to grow non fixed field use also |
| // lib[n?]curses to get the console width (look info source) (so on add a fixed |
| // field flags) |
| size_t formatter:: |
| output_field(ostream & out, field_datum const & datum, |
| format_flags fl, size_t padding, bool hide_immutable) |
| { |
| if (!hide_immutable) { |
| out << string(padding, ' '); |
| |
| field_description const & field(format_map[fl]); |
| string str = (this->*field.formatter)(datum); |
| out << str; |
| |
| // at least one separator char |
| padding = 1; |
| if (str.length() < field.width) |
| padding = field.width - str.length(); |
| } else { |
| field_description const & field(format_map[fl]); |
| padding += field.width; |
| } |
| |
| return padding; |
| } |
| |
| |
| size_t formatter:: |
| output_header_field(ostream & out, format_flags fl, size_t padding) |
| { |
| out << string(padding, ' '); |
| |
| field_description const & field(format_map[fl]); |
| out << field.header_name; |
| |
| // at least one separator char |
| padding = 1; |
| if (field.header_name.length() < field.width) |
| padding = field.width - field.header_name.length(); |
| |
| return padding; |
| } |
| |
| |
| string formatter::format_vma(field_datum const & f) |
| { |
| return get_vma(f.sample.vma, vma_64); |
| } |
| |
| |
| string formatter::format_symb_name(field_datum const & f) |
| { |
| return symbol_names.demangle(f.symbol.name); |
| } |
| |
| |
| string formatter::format_image_name(field_datum const & f) |
| { |
| return get_image_name(f.symbol.image_name, |
| long_filenames |
| ? image_name_storage::int_real_filename |
| : image_name_storage::int_real_basename, |
| extra_found_images); |
| } |
| |
| |
| string formatter::format_app_name(field_datum const & f) |
| { |
| return get_image_name(f.symbol.app_name, |
| long_filenames |
| ? image_name_storage::int_real_filename |
| : image_name_storage::int_real_basename, |
| extra_found_images); |
| } |
| |
| |
| string formatter::format_linenr_info(field_datum const & f) |
| { |
| return get_linenr_info(f.sample.file_loc, long_filenames); |
| } |
| |
| |
| string formatter::format_nr_samples(field_datum const & f) |
| { |
| ostringstream out; |
| out << f.sample.counts[f.pclass]; |
| return out.str(); |
| } |
| |
| |
| string formatter::format_nr_cumulated_samples(field_datum const & f) |
| { |
| if (f.diff == -INFINITY) |
| return "---"; |
| ostringstream out; |
| f.counts.cumulated_samples[f.pclass] += f.sample.counts[f.pclass]; |
| out << f.counts.cumulated_samples[f.pclass]; |
| return out.str(); |
| } |
| |
| |
| string formatter::format_percent(field_datum const & f) |
| { |
| if (f.diff == -INFINITY) |
| return "---"; |
| return get_percent(f.sample.counts[f.pclass], f.counts.total[f.pclass]); |
| } |
| |
| |
| string formatter::format_cumulated_percent(field_datum const & f) |
| { |
| if (f.diff == -INFINITY) |
| return "---"; |
| f.counts.cumulated_percent[f.pclass] += f.sample.counts[f.pclass]; |
| |
| return get_percent(f.counts.cumulated_percent[f.pclass], |
| f.counts.total[f.pclass]); |
| } |
| |
| |
| string formatter::format_percent_details(field_datum const & f) |
| { |
| return get_percent(f.sample.counts[f.pclass], |
| f.counts.total[f.pclass]); |
| } |
| |
| |
| string formatter::format_cumulated_percent_details(field_datum const & f) |
| { |
| f.counts.cumulated_percent_details[f.pclass] += f.sample.counts[f.pclass]; |
| |
| return get_percent(f.counts.cumulated_percent_details[f.pclass], |
| f.counts.total[f.pclass]); |
| } |
| |
| |
| string formatter::format_diff(field_datum const & f) |
| { |
| if (f.diff == INFINITY) |
| return "+++"; |
| else if (f.diff == -INFINITY) |
| return "---"; |
| |
| return ::format_percent(f.diff, percent_int_width, |
| percent_fract_width, true); |
| } |
| |
| |
| void formatter:: |
| do_output(ostream & out, symbol_entry const & symb, sample_entry const & sample, |
| counts_t & c, diff_array_t const & diffs, bool hide_immutable) |
| { |
| size_t padding = 0; |
| |
| // first output the vma field |
| field_datum datum(symb, sample, 0, c, extra_found_images); |
| if (flags & ff_vma) |
| padding = output_field(out, datum, ff_vma, padding, false); |
| |
| // repeated fields for each profile class |
| for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) { |
| field_datum datum(symb, sample, pclass, c, |
| extra_found_images, diffs[pclass]); |
| |
| if (flags & ff_nr_samples) |
| padding = output_field(out, datum, |
| ff_nr_samples, padding, false); |
| |
| if (flags & ff_nr_samples_cumulated) |
| padding = output_field(out, datum, |
| ff_nr_samples_cumulated, padding, false); |
| |
| if (flags & ff_percent) |
| padding = output_field(out, datum, |
| ff_percent, padding, false); |
| |
| if (flags & ff_percent_cumulated) |
| padding = output_field(out, datum, |
| ff_percent_cumulated, padding, false); |
| |
| if (flags & ff_diff) |
| padding = output_field(out, datum, |
| ff_diff, padding, false); |
| |
| if (flags & ff_percent_details) |
| padding = output_field(out, datum, |
| ff_percent_details, padding, false); |
| |
| if (flags & ff_percent_cumulated_details) |
| padding = output_field(out, datum, |
| ff_percent_cumulated_details, padding, false); |
| } |
| |
| // now the remaining field |
| if (flags & ff_linenr_info) |
| padding = output_field(out, datum, ff_linenr_info, |
| padding, false); |
| |
| if (flags & ff_image_name) |
| padding = output_field(out, datum, ff_image_name, |
| padding, hide_immutable); |
| |
| if (flags & ff_app_name) |
| padding = output_field(out, datum, ff_app_name, |
| padding, hide_immutable); |
| |
| if (flags & ff_symb_name) |
| padding = output_field(out, datum, ff_symb_name, |
| padding, hide_immutable); |
| |
| out << "\n"; |
| } |
| |
| |
| opreport_formatter::opreport_formatter(profile_container const & p) |
| : |
| formatter(p.extra_found_images), |
| profile(p), |
| need_details(false) |
| { |
| counts.total = profile.samples_count(); |
| } |
| |
| |
| void opreport_formatter::show_details(bool on_off) |
| { |
| need_details = on_off; |
| } |
| |
| |
| void opreport_formatter::output(ostream & out, symbol_entry const * symb) |
| { |
| do_output(out, *symb, symb->sample, counts); |
| |
| if (need_details) |
| output_details(out, symb); |
| } |
| |
| |
| void opreport_formatter:: |
| output(ostream & out, symbol_collection const & syms) |
| { |
| output_header(out); |
| |
| symbol_collection::const_iterator it = syms.begin(); |
| symbol_collection::const_iterator end = syms.end(); |
| for (; it != end; ++it) |
| output(out, *it); |
| } |
| |
| |
| void opreport_formatter:: |
| output_details(ostream & out, symbol_entry const * symb) |
| { |
| counts_t c = counts; |
| |
| if (!global_percent) |
| c.total = symb->sample.counts; |
| |
| // cumulated percent are relative to current symbol. |
| c.cumulated_samples = count_array_t(); |
| c.cumulated_percent = count_array_t(); |
| |
| sample_container::samples_iterator it = profile.begin(symb); |
| sample_container::samples_iterator end = profile.end(symb); |
| for (; it != end; ++it) { |
| out << " "; |
| do_output(out, *symb, it->second, c, diff_array_t(), true); |
| } |
| } |
| |
| |
| cg_formatter::cg_formatter(callgraph_container const & profile) |
| : |
| formatter(profile.extra_found_images) |
| { |
| counts.total = profile.samples_count(); |
| } |
| |
| |
| void cg_formatter::output(ostream & out, symbol_collection const & syms) |
| { |
| // amount of spacing prefixing child and parent lines |
| string const child_parent_prefix(" "); |
| |
| output_header(out); |
| |
| out << string(79, '-') << endl; |
| |
| symbol_collection::const_iterator it; |
| symbol_collection::const_iterator end = syms.end(); |
| |
| for (it = syms.begin(); it < end; ++it) { |
| cg_symbol const * sym = dynamic_cast<cg_symbol const *>(*it); |
| |
| cg_symbol::children::const_iterator cit; |
| cg_symbol::children::const_iterator cend = sym->callers.end(); |
| |
| counts_t c; |
| if (global_percent) |
| c.total = counts.total; |
| else |
| c.total = sym->total_caller_count; |
| |
| for (cit = sym->callers.begin(); cit != cend; ++cit) { |
| out << child_parent_prefix; |
| do_output(out, *cit, cit->sample, c); |
| } |
| |
| do_output(out, *sym, sym->sample, counts); |
| |
| c = counts_t(); |
| if (global_percent) |
| c.total = counts.total; |
| else |
| c.total = sym->total_callee_count; |
| |
| cend = sym->callees.end(); |
| |
| for (cit = sym->callees.begin(); cit != cend; ++cit) { |
| out << child_parent_prefix; |
| do_output(out, *cit, cit->sample, c); |
| } |
| |
| out << string(79, '-') << endl; |
| } |
| } |
| |
| |
| diff_formatter::diff_formatter(diff_container const & profile, |
| extra_images const & extra) |
| : |
| formatter(extra) |
| { |
| counts.total = profile.samples_count(); |
| } |
| |
| |
| void diff_formatter::output(ostream & out, diff_collection const & syms) |
| { |
| output_header(out); |
| |
| diff_collection::const_iterator it = syms.begin(); |
| diff_collection::const_iterator end = syms.end(); |
| for (; it != end; ++it) |
| do_output(out, *it, it->sample, counts, it->diffs); |
| } |
| |
| // local variables used in generation of XML |
| // buffer details for output later |
| ostringstream bytes_out; |
| |
| // module+symbol table for detecting duplicate symbols |
| map<string, size_t> symbol_data_table; |
| size_t symbol_data_index = 0; |
| |
| /* Return any existing index or add to the table */ |
| size_t xml_get_symbol_index(string const & name) |
| { |
| size_t index = symbol_data_index; |
| map<string, size_t>::iterator it = symbol_data_table.find(name); |
| |
| if (it == symbol_data_table.end()) { |
| symbol_data_table[name] = symbol_data_index++; |
| return index; |
| } |
| |
| return it->second; |
| } |
| |
| |
| class symbol_details_t { |
| public: |
| symbol_details_t() { size = index = 0; id = -1; } |
| int id; |
| size_t size; |
| size_t index; |
| string details; |
| }; |
| |
| typedef growable_vector<symbol_details_t> symbol_details_array_t; |
| symbol_details_array_t symbol_details; |
| size_t detail_table_index = 0; |
| |
| xml_formatter:: |
| xml_formatter(profile_container const * p, |
| symbol_collection & s, extra_images const & extra, |
| string_filter const & sf) |
| : |
| formatter(extra), |
| profile(p), |
| symbols(s), |
| need_details(false), |
| symbol_filter(sf) |
| { |
| if (profile) |
| counts.total = profile->samples_count(); |
| } |
| |
| |
| void xml_formatter:: |
| show_details(bool on_off) |
| { |
| need_details = on_off; |
| } |
| |
| |
| void xml_formatter::output(ostream & out) |
| { |
| xml_support->build_subclasses(out); |
| |
| xml_support->output_program_structure(out); |
| output_symbol_data(out); |
| if (need_details) { |
| out << open_element(DETAIL_TABLE); |
| for (size_t i = 0; i < symbol_details.size(); ++i) { |
| int id = symbol_details[i].id; |
| |
| if (id >= 0) { |
| out << open_element(SYMBOL_DETAILS, true); |
| out << init_attr(TABLE_ID, (size_t)id); |
| out << close_element(NONE, true); |
| out << symbol_details[i].details; |
| out << close_element(SYMBOL_DETAILS); |
| } |
| } |
| out << close_element(DETAIL_TABLE); |
| |
| // output bytesTable |
| out << open_element(BYTES_TABLE); |
| out << bytes_out.str(); |
| out << close_element(BYTES_TABLE); |
| } |
| |
| out << close_element(PROFILE); |
| } |
| |
| bool |
| xml_formatter::get_bfd_object(symbol_entry const * symb, op_bfd * & abfd) const |
| { |
| bool ok = true; |
| |
| string const & image_name = get_image_name(symb->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| if (symb->spu_offset) { |
| // FIXME: what about archive:tmp, actually it's not supported |
| // for spu since oparchive doesn't archive the real file but |
| // in future it would work ? |
| string tmp = get_image_name(symb->embedding_filename, |
| image_name_storage::int_filename, extra_found_images); |
| if (abfd && abfd->get_filename() == tmp) |
| return true; |
| delete abfd; |
| abfd = new op_bfd(symb->spu_offset, tmp, |
| symbol_filter, extra_found_images, ok); |
| } else { |
| if (abfd && abfd->get_filename() == image_name) |
| return true; |
| delete abfd; |
| abfd = new op_bfd(image_name, symbol_filter, |
| extra_found_images, ok); |
| |
| } |
| |
| if (!ok) { |
| report_image_error(image_name, image_format_failure, |
| false, extra_found_images); |
| delete abfd; |
| abfd = 0; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void xml_formatter:: |
| output_the_symbol_data(ostream & out, symbol_entry const * symb, op_bfd * & abfd) |
| { |
| string const name = symbol_names.name(symb->name); |
| assert(name.size() > 0); |
| |
| string const image = get_image_name(symb->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| string const qname = image + ":" + name; |
| map<string, size_t>::iterator sd_it = symbol_data_table.find(qname); |
| |
| if (sd_it != symbol_data_table.end()) { |
| // first time we've seen this symbol |
| out << open_element(SYMBOL_DATA, true); |
| out << init_attr(TABLE_ID, sd_it->second); |
| |
| field_datum datum(*symb, symb->sample, 0, counts, |
| extra_found_images); |
| |
| output_attribute(out, datum, ff_symb_name, NAME); |
| |
| if (flags & ff_linenr_info) { |
| output_attribute(out, datum, ff_linenr_info, SOURCE_FILE); |
| output_attribute(out, datum, ff_linenr_info, SOURCE_LINE); |
| } |
| |
| if (name.size() > 0 && name[0] != '?') { |
| output_attribute(out, datum, ff_vma, STARTING_ADDR); |
| |
| if (need_details) { |
| get_bfd_object(symb, abfd); |
| if (abfd && abfd->symbol_has_contents(symb->sym_index)) |
| xml_support->output_symbol_bytes(bytes_out, symb, sd_it->second, *abfd); |
| } |
| } |
| out << close_element(); |
| |
| // seen so remove (otherwise get several "no symbols") |
| symbol_data_table.erase(qname); |
| } |
| } |
| |
| void xml_formatter::output_cg_children(ostream & out, |
| cg_symbol::children const cg_symb, op_bfd * & abfd) |
| { |
| cg_symbol::children::const_iterator cit; |
| cg_symbol::children::const_iterator cend = cg_symb.end(); |
| |
| for (cit = cg_symb.begin(); cit != cend; ++cit) { |
| string const name = symbol_names.name(cit->name); |
| string const image = get_image_name(cit->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| string const qname = image + ":" + name; |
| map<string, size_t>::iterator sd_it = symbol_data_table.find(qname); |
| |
| if (sd_it != symbol_data_table.end()) { |
| symbol_entry const * child = &(*cit); |
| output_the_symbol_data(out, child, abfd); |
| } |
| } |
| } |
| |
| void xml_formatter::output_symbol_data(ostream & out) |
| { |
| op_bfd * abfd = NULL; |
| sym_iterator it = symbols.begin(); |
| sym_iterator end = symbols.end(); |
| |
| out << open_element(SYMBOL_TABLE); |
| for ( ; it != end; ++it) { |
| symbol_entry const * symb = *it; |
| cg_symbol const * cg_symb = dynamic_cast<cg_symbol const *>(symb); |
| output_the_symbol_data(out, symb, abfd); |
| if (cg_symb) { |
| /* make sure callers/callees are included in SYMBOL_TABLE */ |
| output_cg_children(out, cg_symb->callers, abfd); |
| output_cg_children(out, cg_symb->callees, abfd); |
| } |
| } |
| out << close_element(SYMBOL_TABLE); |
| |
| delete abfd; |
| } |
| |
| string xml_formatter:: |
| output_symbol_details(symbol_entry const * symb, |
| size_t & detail_index, size_t const lo, size_t const hi) |
| { |
| if (!has_sample_counts(symb->sample.counts, lo, hi)) |
| return ""; |
| |
| sample_container::samples_iterator it = profile->begin(symb); |
| sample_container::samples_iterator end = profile->end(symb); |
| |
| ostringstream str; |
| for (; it != end; ++it) { |
| counts_t c; |
| |
| for (size_t p = lo; p <= hi; ++p) { |
| size_t count = it->second.counts[p]; |
| |
| if (count == 0) continue; |
| |
| str << open_element(DETAIL_DATA, true); |
| str << init_attr(TABLE_ID, detail_index++); |
| |
| // first output the vma field |
| field_datum datum(*symb, it->second, 0, c, |
| extra_found_images, 0.0); |
| output_attribute(str, datum, ff_vma, VMA); |
| if (ff_linenr_info) { |
| string sym_file; |
| size_t sym_line; |
| string samp_file; |
| size_t samp_line; |
| string sym_info = get_linenr_info(symb->sample.file_loc, true); |
| string samp_info = get_linenr_info(it->second.file_loc, true); |
| |
| if (extract_linenr_info(samp_info, samp_file, samp_line)) { |
| if (extract_linenr_info(sym_info, sym_file, sym_line)) { |
| // only output source_file if it is different than the symbol's |
| // source file. this can happen with inlined functions in |
| // #included header files |
| if (sym_file != samp_file) |
| str << init_attr(SOURCE_FILE, samp_file); |
| } |
| str << init_attr(SOURCE_LINE, samp_line); |
| } |
| } |
| str << close_element(NONE, true); |
| |
| // output buffered sample data |
| output_sample_data(str, it->second, p); |
| |
| str << close_element(DETAIL_DATA); |
| } |
| } |
| return str.str(); |
| } |
| |
| void xml_formatter:: |
| output_symbol(ostream & out, |
| symbol_entry const * symb, size_t lo, size_t hi, bool is_module) |
| { |
| ostringstream str; |
| // pointless reference to is_module, remove insane compiler warning |
| size_t indx = is_module ? 0 : 1; |
| |
| // output symbol's summary data for each profile class |
| bool got_samples = false; |
| |
| for (size_t p = lo; p <= hi; ++p) { |
| got_samples |= xml_support->output_summary_data(str, |
| symb->sample.counts, p); |
| } |
| |
| if (!got_samples) |
| return; |
| |
| if (cverb << vxml) |
| out << "<!-- symbol_ref=" << symbol_names.name(symb->name) << |
| " -->" << endl; |
| |
| out << open_element(SYMBOL, true); |
| |
| string const name = symbol_names.name(symb->name); |
| assert(name.size() > 0); |
| |
| string const image = get_image_name(symb->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| string const qname = image + ":" + name; |
| |
| indx = xml_get_symbol_index(qname); |
| |
| out << init_attr(ID_REF, indx); |
| |
| if (need_details) { |
| ostringstream details; |
| symbol_details_t & sd = symbol_details[indx]; |
| size_t const detail_lo = sd.index; |
| |
| string detail_str = output_symbol_details(symb, sd.index, lo, hi); |
| |
| if (detail_str.size() > 0) { |
| if (sd.id < 0) |
| sd.id = indx; |
| details << detail_str; |
| } |
| |
| if (sd.index > detail_lo) { |
| sd.details = sd.details + details.str(); |
| out << init_attr(DETAIL_LO, detail_lo); |
| out << init_attr(DETAIL_HI, sd.index-1); |
| } |
| } |
| out << close_element(NONE, true); |
| // output summary |
| out << str.str(); |
| out << close_element(SYMBOL); |
| } |
| |
| |
| void xml_formatter:: |
| output_sample_data(ostream & out, sample_entry const & sample, size_t pclass) |
| { |
| out << open_element(COUNT, true); |
| out << init_attr(CLASS, classes.v[pclass].name); |
| out << close_element(NONE, true); |
| out << sample.counts[pclass]; |
| out << close_element(COUNT); |
| } |
| |
| |
| void xml_formatter:: |
| output_attribute(ostream & out, field_datum const & datum, |
| format_flags fl, tag_t tag) |
| { |
| field_description const & field(format_map[fl]); |
| |
| string str = (this->*field.formatter)(datum); |
| |
| if (!str.empty()) { |
| if (fl == ff_linenr_info && (tag == SOURCE_LINE || tag == SOURCE_FILE)) { |
| string file; |
| size_t line; |
| |
| if (extract_linenr_info(str, file, line)) { |
| if (tag == SOURCE_LINE) |
| out << init_attr(tag, line); |
| else |
| out << init_attr(tag, file); |
| } |
| } else |
| out << " " << init_attr(tag, str); |
| } |
| } |
| |
| xml_cg_formatter:: |
| xml_cg_formatter(callgraph_container const & cg, symbol_collection & s, |
| string_filter const & sf) |
| : |
| xml_formatter(0, s, cg.extra_found_images, sf), |
| callgraph(cg) |
| { |
| counts.total = callgraph.samples_count(); |
| } |
| |
| void xml_cg_formatter:: |
| output_symbol_core(ostream & out, cg_symbol::children const cg_symb, |
| string const selfname, string const qname, |
| size_t lo, size_t hi, bool is_module, tag_t tag) |
| { |
| cg_symbol::children::const_iterator cit; |
| cg_symbol::children::const_iterator cend = cg_symb.end(); |
| |
| for (cit = cg_symb.begin(); cit != cend; ++cit) { |
| string const & module = get_image_name((cit)->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| bool self = false; |
| ostringstream str; |
| size_t indx; |
| |
| // output symbol's summary data for each profile class |
| for (size_t p = lo; p <= hi; ++p) |
| xml_support->output_summary_data(str, cit->sample.counts, p); |
| |
| if (cverb << vxml) |
| out << "<!-- symbol_ref=" << symbol_names.name(cit->name) << |
| " -->" << endl; |
| |
| if (is_module) { |
| out << open_element(MODULE, true); |
| out << init_attr(NAME, module) << close_element(NONE, true); |
| } |
| |
| out << open_element(SYMBOL, true); |
| |
| string const symname = symbol_names.name(cit->name); |
| assert(symname.size() > 0); |
| |
| string const symqname = module + ":" + symname; |
| |
| // Find any self references and handle |
| if ((symname == selfname) && (tag == CALLEES)) { |
| self = true; |
| indx = xml_get_symbol_index(qname); |
| } else { |
| indx = xml_get_symbol_index(symqname); |
| } |
| |
| out << init_attr(ID_REF, indx); |
| |
| if (self) |
| out << init_attr(SELFREF, "true"); |
| |
| out << close_element(NONE, true); |
| out << str.str(); |
| out << close_element(SYMBOL); |
| |
| if (is_module) |
| out << close_element(MODULE); |
| } |
| } |
| |
| |
| void xml_cg_formatter:: |
| output_symbol(ostream & out, |
| symbol_entry const * symb, size_t lo, size_t hi, bool is_module) |
| { |
| cg_symbol const * cg_symb = dynamic_cast<cg_symbol const *>(symb); |
| ostringstream str; |
| size_t indx; |
| |
| // output symbol's summary data for each profile class |
| for (size_t p = lo; p <= hi; ++p) |
| xml_support->output_summary_data(str, symb->sample.counts, p); |
| |
| if (cverb << vxml) |
| out << "<!-- symbol_ref=" << symbol_names.name(symb->name) << |
| " -->" << endl; |
| |
| out << open_element(SYMBOL, true); |
| |
| string const name = symbol_names.name(symb->name); |
| assert(name.size() > 0); |
| |
| string const image = get_image_name(symb->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| string const qname = image + ":" + name; |
| |
| string const selfname = symbol_names.demangle(symb->name) + " [self]"; |
| |
| indx = xml_get_symbol_index(qname); |
| |
| out << init_attr(ID_REF, indx); |
| |
| out << close_element(NONE, true); |
| |
| out << open_element(CALLERS); |
| if (cg_symb) |
| output_symbol_core(out, cg_symb->callers, selfname, qname, lo, hi, is_module, CALLERS); |
| out << close_element(CALLERS); |
| |
| out << open_element(CALLEES); |
| if (cg_symb) |
| output_symbol_core(out, cg_symb->callees, selfname, qname, lo, hi, is_module, CALLEES); |
| |
| out << close_element(CALLEES); |
| |
| // output summary |
| out << str.str(); |
| out << close_element(SYMBOL); |
| } |
| |
| } // namespace format_output |