1/** 2 * @file xml_utils.cpp 3 * utility routines for generating XML 4 * 5 * @remark Copyright 2006 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author Dave Nomura 9 */ 10 11#include <iostream> 12#include <sstream> 13 14#include "xml_utils.h" 15#include "format_output.h" 16#include "arrange_profiles.h" 17#include "op_bfd.h" 18#include "cverb.h" 19 20using namespace std; 21 22bool want_xml = false; 23 24size_t nr_classes = 0; 25size_t nr_cpus = 0; 26size_t nr_events = 0; 27sym_iterator symbols_begin; 28sym_iterator symbols_end; 29// handle on xml_formatter object 30format_output::xml_formatter * xml_out; 31xml_utils * xml_support; 32size_t xml_utils::events_index = 0; 33bool xml_utils::has_nonzero_masks = false; 34ostringstream xml_options; 35 36 37 38namespace { 39 40bool has_separated_cpu_info() 41{ 42 return classes.v[0].ptemplate.cpu != "all"; 43} 44 45 46string get_event_num(size_t pclass) 47{ 48 return classes.v[pclass].ptemplate.event; 49} 50 51 52size_t get_next_event_num_pclass(size_t start) 53{ 54 string cur_event = get_event_num(start); 55 size_t i; 56 for (i = start; 57 i < nr_classes && get_event_num(i) == cur_event; 58 ++i) ; 59 return i; 60} 61 62 63void dump_symbol(string const & prefix, sym_iterator it, bool want_nl = true) 64{ 65 if (it == symbols_end) 66 cverb << vxml << prefix << "END"; 67 else 68 cverb << vxml << prefix << symbol_names.name((*it)->name); 69 if (want_nl) 70 cverb << vxml << endl; 71} 72 73 74void dump_symbols(string const & prefix, sym_iterator b, sym_iterator e) 75{ 76 if (b == (sym_iterator)0) 77 return; 78 79 for (sym_iterator it = b; it != e; ++it) 80 dump_symbol(prefix, it, true); 81} 82 83 84 85void dump_classes() 86{ 87 cverb << vxml << "<!-- classes dump" << endl; 88 cverb << vxml << classes.event; 89 cverb << vxml << "classes.size= " << classes.v.size() << endl; 90 for (size_t i = 0; i < classes.v.size(); ++i) { 91 cverb << vxml << "--- class " << i << ":" << classes.v[i].name << " ---" << endl; 92 cverb << vxml << classes.v[i].ptemplate; 93 } 94 cverb << vxml << "-->" << endl; 95} 96 97 98bool has_separated_thread_info() 99{ 100 return classes.v[0].ptemplate.tid != "all"; 101} 102 103 104string get_cpu_num(size_t pclass) 105{ 106 return classes.v[pclass].ptemplate.cpu; 107} 108 109 110}; // anonymous namespace 111 112xml_utils::xml_utils(format_output::xml_formatter * xo, 113 symbol_collection const & s, size_t nc, 114 extra_images const & extra) 115 : 116 has_subclasses(false), 117 bytes_index(0), 118 extra_found_images(extra) 119{ 120 xml_out = xo; 121 nr_classes = nc; 122 symbols_begin = s.begin(); 123 symbols_end = s.end(); 124 multiple_events = get_next_event_num_pclass(0) != nr_classes; 125 126 if (has_separated_cpu_info()) { 127 size_t cpus = 0; 128 // count number of cpus 129 for (size_t p = 0; p < nr_classes; ++p) { 130 size_t cpu = atoi(classes.v[p].ptemplate.cpu.c_str()); 131 if (cpu > cpus) cpus = cpu; 132 } 133 // cpus names start with 0 134 nr_cpus = cpus + 1; 135 } 136} 137 138 139string xml_utils::get_timer_setup(size_t count) 140{ 141 return open_element(TIMER_SETUP, true) + 142 init_attr(RTC_INTERRUPTS, count) + close_element(); 143} 144 145 146string xml_utils::get_event_setup(string event, size_t count, 147 string unit_mask) 148{ 149 ostringstream str; 150 151 str << open_element(EVENT_SETUP, true); 152 str << init_attr(TABLE_ID, events_index++); 153 str << init_attr(EVENT_NAME, event); 154 if (unit_mask.size() != 0) str << init_attr(UNIT_MASK, unit_mask); 155 str << init_attr(SETUP_COUNT, (size_t)count) + close_element(); 156 return str.str(); 157} 158 159 160string xml_utils::get_profile_header(string cpu_name, double const speed) 161{ 162 ostringstream str; 163 string cpu_type; 164 string processor; 165 string::size_type slash_pos = cpu_name.find("/"); 166 167 if (slash_pos == string::npos) { 168 cpu_type = cpu_name; 169 processor = ""; 170 } else { 171 cpu_type = cpu_name.substr(0, slash_pos); 172 processor = cpu_name.substr(slash_pos+1); 173 } 174 175 str << init_attr(CPU_NAME, cpu_type) << endl; 176 if (processor.size() > 0) 177 str << init_attr(PROCESSOR, string(processor)) << endl; 178 if (nr_cpus > 1) str << init_attr(SEPARATED_CPUS, nr_cpus) << endl; 179 str << init_attr(MHZ, speed) << endl; 180 181 return str.str(); 182} 183 184 185void xml_utils::set_nr_cpus(size_t cpus) 186{ 187 nr_cpus = cpus; 188} 189 190void xml_utils::set_nr_events(size_t events) 191{ 192 nr_events = events; 193} 194 195void xml_utils::set_has_nonzero_masks() 196{ 197 has_nonzero_masks = true; 198} 199 200 201void xml_utils::add_option(tag_t tag, string const & value) 202{ 203 xml_options << init_attr(tag, value); 204} 205 206 207void xml_utils::add_option(tag_t tag, list<string> const & value) 208{ 209 list<string>::const_iterator begin = value.begin(); 210 list<string>::const_iterator end = value.end(); 211 list<string>::const_iterator cit = begin; 212 ostringstream str; 213 214 for (; cit != end; ++cit) { 215 if (cit != begin) 216 str << ","; 217 str << *cit; 218 } 219 xml_options << init_attr(tag, str.str()); 220} 221 222 223void xml_utils::add_option(tag_t tag, vector<string> const & value) 224{ 225 vector<string>::const_iterator begin = value.begin(); 226 vector<string>::const_iterator end = value.end(); 227 vector<string>::const_iterator cit = begin; 228 ostringstream str; 229 230 for (; cit != end; ++cit) { 231 if (cit != begin) 232 str << ","; 233 str << *cit; 234 } 235 xml_options << init_attr(tag, str.str()); 236} 237 238 239void xml_utils::add_option(tag_t tag, bool value) 240{ 241 xml_options << init_attr(tag, (value ? "true" : "false")); 242} 243 244 245void xml_utils::output_xml_header(string const & command_options, 246 string const & cpu_info, string const & events) 247{ 248 // the integer portion indicates the schema version and should change 249 // both here and in the schema file when major changes are made to 250 // the schema. changes to opreport, or minor changes to the schema 251 // can be indicated by changes to the fraction part. 252 string const schema_version = "3.0"; 253 254 // This is the XML version, not schema version. 255 string const xml_header = "<?xml version=\"1.0\" ?>"; 256 257 cout << xml_header << endl; 258 cout << open_element(PROFILE, true); 259 cout << init_attr(SCHEMA_VERSION, schema_version); 260 261 cout << cpu_info; 262 cout << init_attr(TITLE, "opreport " + command_options); 263 cout << close_element(NONE, true); 264 265 cout << open_element(OPTIONS, true) << xml_options.str(); 266 cout << close_element(); 267 268 cout << open_element(SETUP) << events; 269 cout << close_element(SETUP) << endl; 270} 271 272class subclass_info_t { 273public: 274 string unitmask; 275 string subclass_name; 276}; 277 278typedef growable_vector<subclass_info_t> subclass_array_t; 279typedef growable_vector<subclass_array_t> event_subclass_t; 280typedef growable_vector<event_subclass_t> cpu_subclass_t; 281 282void xml_utils::build_subclasses(ostream & out) 283{ 284 size_t subclasses = 0; 285 string subclass_name; 286 // when --separate=cpu we will have an event_subclass array for each cpu 287 cpu_subclass_t cpu_subclasses; 288 289 event_subclass_t event_subclasses; 290 291 if (nr_cpus <= 1 && nr_events <= 1 && !has_nonzero_masks) 292 return; 293 294 out << open_element(CLASSES); 295 for (size_t i = 0; i < classes.v.size(); ++i) { 296 profile_class & pclass = classes.v[i]; 297 size_t event = atoi(pclass.ptemplate.event.c_str()); 298 299 subclass_array_t * sc_ptr; 300 301 // select the right subclass array 302 if (nr_cpus == 1) { 303 sc_ptr = &event_subclasses[event]; 304 } else { 305 size_t cpu = atoi(pclass.ptemplate.cpu.c_str()); 306 sc_ptr = &cpu_subclasses[cpu][event]; 307 } 308 309 // search for an existing unitmask 310 subclass_name = ""; 311 for (size_t j = 0; j < sc_ptr->size(); ++j) { 312 if ((*sc_ptr)[j].unitmask == pclass.ptemplate.unitmask) { 313 subclass_name = (*sc_ptr)[j].subclass_name; 314 break; 315 } 316 } 317 318 if (subclass_name.size() == 0) { 319 ostringstream str; 320 size_t new_index = sc_ptr->size(); 321 322 // no match found, create a new entry 323 str << "c" << subclasses++; 324 subclass_name = str.str(); 325 (*sc_ptr)[new_index].unitmask = pclass.ptemplate.unitmask; 326 (*sc_ptr)[new_index].subclass_name = subclass_name; 327 out << open_element(CLASS, true); 328 out << init_attr(NAME, subclass_name); 329 if (nr_cpus > 1) 330 out << init_attr(CPU_NUM, pclass.ptemplate.cpu); 331 if (nr_events > 1) 332 out << init_attr(EVENT_NUM, event); 333 if (has_nonzero_masks) 334 out << init_attr(EVENT_MASK, pclass.ptemplate.unitmask); 335 out << close_element(); 336 } 337 338 pclass.name = subclass_name; 339 } 340 out << close_element(CLASSES); 341 has_subclasses = true; 342} 343 344 345string 346get_counts_string(count_array_t const & counts, size_t begin, size_t end) 347{ 348 ostringstream str; 349 bool got_count = false; 350 351 // if no cpu separation then return a simple count, omit zero counts 352 if (nr_cpus == 1) { 353 size_t count = counts[begin]; 354 if (count == 0) 355 return ""; 356 str << count; 357 return str.str(); 358 } 359 360 for (size_t p = begin; p != end; ++p) { 361 size_t count = counts[p]; 362 if (p != begin) str << ","; 363 if (count != 0) { 364 got_count = true; 365 str << count; 366 } 367 } 368 return got_count ? str.str() : ""; 369} 370 371 372void 373xml_utils::output_symbol_bytes(ostream & out, symbol_entry const * symb, 374 size_t sym_id, op_bfd const & abfd) 375{ 376 size_t size = symb->size; 377 scoped_array<unsigned char> contents(new unsigned char[size]); 378 if (abfd.get_symbol_contents(symb->sym_index, contents.get())) { 379 string const name = symbol_names.name(symb->name); 380 out << open_element(BYTES, true) << init_attr(TABLE_ID, sym_id); 381 out << close_element(NONE, true); 382 for (size_t i = 0; i < size; ++i) { 383 char hex_map[] = "0123456789ABCDEF"; 384 char hex[2]; 385 hex[0] = hex_map[(contents[i] >> 4) & 0xf]; 386 hex[1] = hex_map[contents[i] & 0xf]; 387 out << hex[0] << hex[1]; 388 } 389 out << close_element(BYTES); 390 } 391} 392 393 394bool 395xml_utils::output_summary_data(ostream & out, count_array_t const & summary, size_t pclass) 396{ 397 size_t const count = summary[pclass]; 398 399 if (count == 0) 400 return false; 401 402 out << open_element(COUNT, has_subclasses); 403 if (has_subclasses) { 404 out << init_attr(CLASS, classes.v[pclass].name); 405 out << close_element(NONE, true); 406 } 407 out << count; 408 out << close_element(COUNT); 409 return true; 410} 411 412class module_info { 413public: 414 module_info() 415 { lo = hi = 0; name = ""; begin = end = (sym_iterator)0;} 416 void dump(); 417 void build_module(string const & n, sym_iterator it, 418 size_t l, size_t h); 419 string get_name() { return name; } 420 void set_lo(size_t l) { lo = l; } 421 void set_hi(size_t h) { hi = h; } 422 count_array_t const & get_summary() { return summary; } 423 void set_begin(sym_iterator b); 424 void set_end(sym_iterator e); 425 void add_to_summary(count_array_t const & counts); 426 void output(ostream & out); 427 bool is_closed(string const & n); 428protected: 429 void output_summary(ostream & out); 430 void output_symbols(ostream & out, bool is_module); 431 432 string name; 433 sym_iterator begin; 434 sym_iterator end; 435 436 // summary sample data 437 count_array_t summary; 438 439 // range of profile classes approprate for this module 440 size_t lo; 441 size_t hi; 442}; 443 444class thread_info : public module_info { 445public: 446 thread_info() { nr_modules = 0; } 447 448 void build_thread(string const & tid, size_t l, size_t h); 449 bool add_modules(string const & module, sym_iterator it); 450 void add_module_symbol(string const & n, sym_iterator it); 451 void summarize(); 452 void set_end(sym_iterator end); 453 string const get_tid() { return thread_id; } 454 void output(ostream & out); 455 void dump(); 456private: 457 // indices into the classes array applicable to this process 458 size_t nr_modules; 459 string thread_id; 460 growable_vector<module_info> my_modules; 461}; 462 463class process_info : public module_info { 464public: 465 process_info() { nr_threads = 0; } 466 void build_process(string const & pid, size_t l, size_t h); 467 void add_thread(string const & tid, size_t l, size_t h); 468 void add_modules(string const & module, 469 string const & app_name, sym_iterator it); 470 void summarize(); 471 void set_end(sym_iterator end); 472 void output(ostream & out); 473 void dump(); 474private: 475 size_t nr_threads; 476 string process_id; 477 growable_vector<thread_info> my_threads; 478 479}; 480class process_root_info { 481public: 482 process_root_info() { nr_processes = 0; } 483 process_info * add_process(string const & pid, size_t lo, size_t hi); 484 void add_modules(string const & module, string const & app_name, 485 sym_iterator it); 486 void summarize(); 487 void summarize_processes(extra_images const & extra_found_images); 488 void set_process_end(); 489 void output_process_symbols(ostream & out); 490 void dump_processes(); 491private: 492 size_t nr_processes; 493 494 growable_vector<process_info> processes; 495}; 496 497class binary_info : public module_info { 498public: 499 binary_info() { nr_modules = 0; } 500 void output(ostream & out); 501 binary_info * build_binary(string const & n); 502 void add_module_symbol(string const & module, string const & app, 503 sym_iterator it); 504 void close_binary(sym_iterator it); 505 void dump(); 506private: 507 size_t nr_modules; 508 509 growable_vector<module_info> my_modules; 510}; 511 512 513class binary_root_info { 514public: 515 binary_root_info() { nr_binaries = 0; } 516 binary_info * add_binary(string const & n, sym_iterator it); 517 void summarize_binaries(extra_images const & extra_found_images); 518 void output_binary_symbols(ostream & out); 519 void dump_binaries(); 520private: 521 size_t nr_binaries; 522 523 growable_vector<binary_info> binaries; 524}; 525 526static process_root_info processes_root; 527static binary_root_info binaries_root; 528 529 530void module_info:: 531build_module(string const & n, sym_iterator it, size_t l, size_t h) 532{ 533 name = n; 534 begin = it; 535 lo = l; 536 hi = h; 537} 538 539 540void module_info::add_to_summary(count_array_t const & counts) 541{ 542 for (size_t pclass = lo ; pclass <= hi; ++pclass) 543 summary[pclass] += counts[pclass]; 544} 545 546 547void module_info::set_begin(sym_iterator b) 548{ 549 if (begin == (sym_iterator)0) 550 begin = b; 551} 552 553 554void module_info::set_end(sym_iterator e) 555{ 556 if (end == (sym_iterator)0) 557 end = e; 558} 559 560 561bool module_info::is_closed(string const & n) 562{ 563 return (name == n) && end != (sym_iterator)0; 564} 565 566 567void module_info::dump() 568{ 569 cverb << vxml << " module:class(" << lo << "," << hi << ")="; 570 cverb << vxml << name << endl; 571 dump_symbols(" ", begin, end); 572} 573 574 575void module_info::output(ostream & out) 576{ 577 out << open_element(MODULE, true); 578 out << init_attr(NAME, name) << close_element(NONE, true); 579 output_summary(out); 580 output_symbols(out, true); 581 out << close_element(MODULE); 582} 583 584 585void module_info::output_summary(ostream & out) 586{ 587 for (size_t p = lo; p <= hi; ++p) 588 (void)xml_support->output_summary_data(out, summary, p); 589} 590 591 592void module_info::output_symbols(ostream & out, bool is_module) 593{ 594 if (begin == (sym_iterator)0) 595 return; 596 597 for (sym_iterator it = begin; it != end; ++it) 598 xml_out->output_symbol(out, *it, lo, hi, is_module); 599} 600 601 602void binary_info::close_binary(sym_iterator it) 603{ 604 set_end(it); 605 if (nr_modules > 0) { 606 module_info & m = my_modules[nr_modules-1]; 607 m.set_end(it); 608 } 609} 610 611 612void binary_info::dump() 613{ 614 cverb << vxml << "app_name=" << name << endl; 615 if (begin != (sym_iterator)0) 616 dump_symbols(" ", begin, end); 617 618 for (size_t i = 0; i < nr_modules; ++i) 619 my_modules[i].dump(); 620} 621 622 623void binary_info:: 624add_module_symbol(string const & module, string const & app, 625 sym_iterator it) 626{ 627 size_t m = nr_modules; 628 629 if (module == app) { 630 // set begin symbol for binary if not set 631 set_begin(it); 632 633 if (m > 0) { 634 // close out current module 635 module_info & mod = my_modules[m-1]; 636 mod.set_end(it); 637 } 638 639 // add symbol count to binary count 640 add_to_summary((*it)->sample.counts); 641 return; 642 } 643 644 string current_module_name = (m == 0 ? "" : my_modules[m-1].get_name()); 645 if (module != current_module_name) { 646 // we have a module distinct from it's binary: --separate=lib 647 // and this is the first symbol for this module 648 if (m != 0) { 649 // close out current module 650 module_info & mod = my_modules[m-1]; 651 mod.set_end(it); 652 add_to_summary(mod.get_summary()); 653 } 654 655 // mark end of enclosing binary symbols if there have been any 656 // NOTE: it is possible for the binary's symbols to follow its 657 // module symbols 658 if (begin != (sym_iterator)0 && end == (sym_iterator)0) 659 set_end(it); 660 661 // build the new module 662 nr_modules++; 663 my_modules[m].build_module(module, it, 0, nr_classes-1); 664 } 665 666 // propagate this symbols counts to the module 667 my_modules[nr_modules-1].add_to_summary((*it)->sample.counts); 668} 669 670 671void binary_root_info:: 672summarize_binaries(extra_images const & extra_found_images) 673{ 674 binary_info * current_binary = 0; 675 string current_binary_name = ""; 676 677 for (sym_iterator it = symbols_begin ; it != symbols_end; ++it) { 678 string binary = get_image_name((*it)->app_name, 679 image_name_storage::int_filename, extra_found_images); 680 string module = get_image_name((*it)->image_name, 681 image_name_storage::int_filename, extra_found_images); 682 683 if (binary != current_binary_name) { 684 current_binary = binaries_root.add_binary(binary, it); 685 current_binary_name = binary; 686 } 687 688 current_binary->add_module_symbol(module, binary, it); 689 } 690 691 // close out last binary and module 692 current_binary->close_binary(symbols_end); 693} 694 695 696process_info * 697process_root_info::add_process(string const & pid, size_t lo, size_t hi) 698{ 699 processes[nr_processes].build_process(pid, lo, hi); 700 return &processes[nr_processes++]; 701} 702 703 704void process_root_info:: 705add_modules(string const & module, string const & app_name, 706 sym_iterator it) 707{ 708 for (size_t p = 0; p < nr_processes; ++p) 709 processes[p].add_modules(module, app_name, it); 710} 711 712 713 714void process_root_info::summarize() 715{ 716 for (size_t p = 0; p < nr_processes; ++p) 717 processes[p].summarize(); 718} 719 720 721void process_root_info:: 722summarize_processes(extra_images const & extra_found_images) 723{ 724 // add modules to the appropriate threads in the process hierarchy 725 for (sym_iterator it = symbols_begin ; it != symbols_end; ++it) { 726 string binary = get_image_name((*it)->app_name, 727 image_name_storage::int_filename, extra_found_images); 728 string module = get_image_name((*it)->image_name, 729 image_name_storage::int_filename, extra_found_images); 730 731 processes_root.add_modules(module, binary, it); 732 } 733 734 // set end symbol boundary for all modules in all threads 735 processes_root.set_process_end(); 736 737 // propagate summaries to process/thread 738 processes_root.summarize(); 739} 740 741 742void process_root_info::set_process_end() 743{ 744 for (size_t p = 0; p < nr_processes; ++p) 745 processes[p].set_end(symbols_end); 746} 747 748void process_root_info::output_process_symbols(ostream & out) 749{ 750 for (size_t p = 0; p < nr_processes; ++p) 751 processes[p].output(out); 752} 753 754 755void process_root_info::dump_processes() 756{ 757 cverb << vxml << "<!-- processes_dump:" << endl; 758 for (size_t p = 0; p < nr_processes; ++p) 759 processes[p].dump(); 760 cverb << vxml << "end processes_dump -->" << endl; 761} 762 763binary_info * 764binary_info::build_binary(string const & n) 765{ 766 name = n; 767 lo = 0; 768 hi = nr_classes-1; 769 return this; 770} 771 772 773void binary_info::output(ostream & out) 774{ 775 out << open_element(BINARY, true); 776 out << init_attr(NAME, name) << close_element(NONE, true); 777 778 output_summary(out); 779 output_symbols(out, false); 780 for (size_t a = 0; a < nr_modules; ++a) 781 my_modules[a].output(out); 782 783 out << close_element(BINARY); 784} 785 786 787binary_info * 788binary_root_info::add_binary(string const & n, sym_iterator it) 789{ 790 size_t a = nr_binaries++; 791 792 // close out previous binary and module 793 if (a > 0) binaries[a-1].close_binary(it); 794 return binaries[a].build_binary(n); 795} 796 797 798void binary_root_info::output_binary_symbols(ostream & out) 799{ 800 for (size_t a = 0; a < nr_binaries; ++a) 801 binaries[a].output(out); 802} 803 804 805void binary_root_info::dump_binaries() 806{ 807 cverb << vxml << "<!-- binaries_dump:" << endl; 808 for (size_t p = 0; p < nr_binaries; ++p) 809 binaries[p].dump(); 810 cverb << vxml << "end processes_dump -->" << endl; 811} 812 813 814void process_info::build_process(string const & pid, size_t l, size_t h) 815{ 816 process_id = pid; 817 lo = l; 818 hi = h; 819} 820 821 822void process_info::add_thread(string const & tid, size_t l, size_t h) 823{ 824 my_threads[nr_threads++].build_thread(tid, l, h); 825} 826 827 828void process_info::add_modules(string const & module, 829 string const & app_name, sym_iterator it) 830{ 831 bool added = false; 832 for (size_t t = 0; t < nr_threads; ++t) 833 added |= my_threads[t].add_modules(module, it); 834 if (added && name.size() == 0) name = app_name; 835} 836 837 838void process_info::summarize() 839{ 840 for (size_t t = 0; t < nr_threads; ++t) { 841 thread_info & thr = my_threads[t]; 842 thr.summarize(); 843 add_to_summary(thr.get_summary()); 844 } 845} 846 847 848void thread_info::build_thread(string const & tid, size_t l, size_t h) 849{ 850 thread_id = tid; 851 lo = l; 852 hi = h; 853} 854 855 856void thread_info::summarize() 857{ 858 for (size_t m = 0; m < nr_modules; ++m) 859 add_to_summary(my_modules[m].get_summary()); 860} 861 862 863void thread_info::set_end(sym_iterator end) 864{ 865 for (size_t m = 0; m < nr_modules; ++m) 866 my_modules[m].set_end(end); 867} 868 869 870void thread_info::add_module_symbol(string const & n, sym_iterator it) 871{ 872 module_info & m = my_modules[nr_modules++]; 873 m.build_module(n, it, lo, hi); 874 m.add_to_summary((*it)->sample.counts); 875} 876 877void thread_info::output(ostream & out) 878{ 879 ostringstream thread_summary; 880 ostringstream modules_output; 881 882 output_summary(thread_summary); 883 884 for (size_t m = 0; m < nr_modules; ++m) 885 my_modules[m].output(modules_output); 886 887 // ignore threads with no sample data 888 if (modules_output.str().size() == 0 && thread_summary.str().size() == 0) 889 return; 890 891 out << open_element(THREAD, true); 892 out << init_attr(THREAD_ID, thread_id) << close_element(NONE, true); 893 out << thread_summary.str(); 894 out << modules_output.str(); 895 out << close_element(THREAD); 896} 897 898 899bool thread_info::add_modules(string const & module, sym_iterator it) 900{ 901 string old_name = 902 (nr_modules == 0 ? "" : my_modules[nr_modules-1].get_name()); 903 if (nr_modules > 0 && old_name != module) { 904 module_info & m = my_modules[nr_modules-1]; 905 // close out previous module if it hasn't already been closed out 906 if (!m.is_closed(old_name)) 907 m.set_end(it); 908 } 909 910 // add a new module for this symbol if it has a non-zero count 911 if (nr_modules == 0 || module != old_name) { 912 if (has_sample_counts((*it)->sample.counts, lo, hi)) { 913 add_module_symbol(module, it); 914 return true; 915 } 916 } else { 917 // propagate symbols count to module 918 my_modules[nr_modules-1].add_to_summary((*it)->sample.counts); 919 } 920 return false; 921} 922 923 924void thread_info::dump() 925{ 926 cverb << vxml << "tid=" << thread_id << endl; 927 for (size_t i = 0; i < nr_modules; ++i) 928 my_modules[i].dump(); 929} 930 931 932void process_info::set_end(sym_iterator end) 933{ 934 for (size_t t = 0; t < nr_threads; ++t) 935 my_threads[t].set_end(end); 936} 937 938 939void process_info::output(ostream & out) 940{ 941 ostringstream process_summary; 942 ostringstream thread_output; 943 944 output_summary(process_summary); 945 946 for (size_t t = 0; t < nr_threads; ++t) 947 my_threads[t].output(thread_output); 948 949 // ignore processes with no sample data 950 if (thread_output.str().size() == 0 && process_summary.str().size() == 0) 951 return; 952 953 out << open_element(PROCESS, true); 954 out << init_attr(PROC_ID, process_id); 955 out << init_attr(NAME, name) << close_element(NONE, true); 956 out << process_summary.str(); 957 out << thread_output.str(); 958 out << close_element(PROCESS); 959} 960 961 962void process_info::dump() 963{ 964 cverb << vxml << "pid=" << process_id << " app=" << name << endl; 965 for (size_t i = 0; i < nr_threads; ++i) 966 my_threads[i].dump(); 967} 968 969size_t get_next_tgid_pclass(size_t start) 970{ 971 string cur_tgid = classes.v[start].ptemplate.tgid; 972 size_t i = start; 973 for (i = start; 974 i < nr_classes && classes.v[i].ptemplate.tgid == cur_tgid; 975 ++i) ; 976 return i; 977} 978 979 980size_t get_next_tid_pclass(size_t start) 981{ 982 string cur_tid = classes.v[start].ptemplate.tid; 983 size_t i; 984 for (i = start; 985 i < nr_classes && classes.v[i].ptemplate.tid == cur_tid; 986 ++i) ; 987 return i; 988} 989 990 991// build the process/thread/module hierarchy that will allow us later 992// to collect the summary sample data at each level and then 993// traverse the hierarchy to intersperse the summary data for the 994// symbols 995void build_process_tree() 996{ 997 size_t tgid = 0; 998 size_t tid = 0; 999 1000 // build the structure representing the process/thread/module hierarchy 1001 // for holding the summary data associated with each level and to be 1002 // traversed when outputting the body of the XML 1003 do { 1004 size_t next_tgid = get_next_tgid_pclass(tgid); 1005 string const tgid_str = classes.v[tgid].ptemplate.tgid; 1006 1007 process_info * p = processes_root.add_process(tgid_str, tgid, next_tgid-1); 1008 1009 do { 1010 size_t next_tid = get_next_tid_pclass(tid); 1011 1012 // build array of threads associated with this process 1013 p->add_thread(classes.v[tid].ptemplate.tid, tid, next_tid-1); 1014 tid = next_tid; 1015 } while (tid != next_tgid); 1016 tgid = next_tgid; 1017 } while (tgid != nr_classes); 1018} 1019 1020void xml_utils::output_program_structure(ostream & out) 1021{ 1022 1023 if (cverb << vxml) 1024 dump_classes(); 1025 1026 if (has_separated_thread_info()) { 1027 build_process_tree(); 1028 processes_root.summarize_processes(extra_found_images); 1029 if (cverb << vxml) 1030 processes_root.dump_processes(); 1031 processes_root.output_process_symbols(out); 1032 } else { 1033 binaries_root.summarize_binaries(extra_found_images); 1034 if (cverb << vxml) 1035 binaries_root.dump_binaries(); 1036 binaries_root.output_binary_symbols(out); 1037 } 1038} 1039