Home | History | Annotate | Download | only in libpp
      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