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