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 20 using namespace std; 21 22 bool want_xml = false; 23 24 size_t nr_classes = 0; 25 size_t nr_cpus = 0; 26 size_t nr_events = 0; 27 sym_iterator symbols_begin; 28 sym_iterator symbols_end; 29 // handle on xml_formatter object 30 format_output::xml_formatter * xml_out; 31 xml_utils * xml_support; 32 size_t xml_utils::events_index = 0; 33 bool xml_utils::has_nonzero_masks = false; 34 ostringstream xml_options; 35 36 37 38 namespace { 39 40 bool has_separated_cpu_info() 41 { 42 return classes.v[0].ptemplate.cpu != "all"; 43 } 44 45 46 string get_event_num(size_t pclass) 47 { 48 return classes.v[pclass].ptemplate.event; 49 } 50 51 52 size_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 63 void 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 74 void 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 85 void 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 98 bool has_separated_thread_info() 99 { 100 return classes.v[0].ptemplate.tid != "all"; 101 } 102 103 104 string get_cpu_num(size_t pclass) 105 { 106 return classes.v[pclass].ptemplate.cpu; 107 } 108 109 110 }; // anonymous namespace 111 112 xml_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 139 string 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 146 string 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 160 string 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 185 void xml_utils::set_nr_cpus(size_t cpus) 186 { 187 nr_cpus = cpus; 188 } 189 190 void xml_utils::set_nr_events(size_t events) 191 { 192 nr_events = events; 193 } 194 195 void xml_utils::set_has_nonzero_masks() 196 { 197 has_nonzero_masks = true; 198 } 199 200 201 void xml_utils::add_option(tag_t tag, string const & value) 202 { 203 xml_options << init_attr(tag, value); 204 } 205 206 207 void 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 223 void 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 239 void xml_utils::add_option(tag_t tag, bool value) 240 { 241 xml_options << init_attr(tag, (value ? "true" : "false")); 242 } 243 244 245 void 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 272 class subclass_info_t { 273 public: 274 string unitmask; 275 string subclass_name; 276 }; 277 278 typedef growable_vector<subclass_info_t> subclass_array_t; 279 typedef growable_vector<subclass_array_t> event_subclass_t; 280 typedef growable_vector<event_subclass_t> cpu_subclass_t; 281 282 void 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 345 string 346 get_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 372 void 373 xml_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 394 bool 395 xml_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 412 class module_info { 413 public: 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); 428 protected: 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 444 class thread_info : public module_info { 445 public: 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(); 456 private: 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 463 class process_info : public module_info { 464 public: 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(); 474 private: 475 size_t nr_threads; 476 string process_id; 477 growable_vector<thread_info> my_threads; 478 479 }; 480 class process_root_info { 481 public: 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(); 491 private: 492 size_t nr_processes; 493 494 growable_vector<process_info> processes; 495 }; 496 497 class binary_info : public module_info { 498 public: 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(); 506 private: 507 size_t nr_modules; 508 509 growable_vector<module_info> my_modules; 510 }; 511 512 513 class binary_root_info { 514 public: 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(); 520 private: 521 size_t nr_binaries; 522 523 growable_vector<binary_info> binaries; 524 }; 525 526 static process_root_info processes_root; 527 static binary_root_info binaries_root; 528 529 530 void module_info:: 531 build_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 540 void 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 547 void module_info::set_begin(sym_iterator b) 548 { 549 if (begin == (sym_iterator)0) 550 begin = b; 551 } 552 553 554 void module_info::set_end(sym_iterator e) 555 { 556 if (end == (sym_iterator)0) 557 end = e; 558 } 559 560 561 bool module_info::is_closed(string const & n) 562 { 563 return (name == n) && end != (sym_iterator)0; 564 } 565 566 567 void module_info::dump() 568 { 569 cverb << vxml << " module:class(" << lo << "," << hi << ")="; 570 cverb << vxml << name << endl; 571 dump_symbols(" ", begin, end); 572 } 573 574 575 void 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 585 void 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 592 void 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 602 void 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 612 void 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 623 void binary_info:: 624 add_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 671 void binary_root_info:: 672 summarize_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 696 process_info * 697 process_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 704 void process_root_info:: 705 add_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 714 void process_root_info::summarize() 715 { 716 for (size_t p = 0; p < nr_processes; ++p) 717 processes[p].summarize(); 718 } 719 720 721 void process_root_info:: 722 summarize_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 742 void 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 748 void 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 755 void 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 763 binary_info * 764 binary_info::build_binary(string const & n) 765 { 766 name = n; 767 lo = 0; 768 hi = nr_classes-1; 769 return this; 770 } 771 772 773 void 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 787 binary_info * 788 binary_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 798 void 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 805 void 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 814 void 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 822 void 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 828 void 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 838 void 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 848 void 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 856 void 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 863 void 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 870 void 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 877 void 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 899 bool 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 924 void 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 932 void 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 939 void 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 962 void 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 969 size_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 980 size_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 995 void 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 1020 void 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