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