1/**
2 * @file format_output.cpp
3 * outputting format for symbol lists
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Philippe Elie
9 * @author John Levon
10 */
11
12/* older glibc has C99 INFINITY in _GNU_SOURCE */
13#ifndef _GNU_SOURCE
14#define _GNU_SOURCE
15#endif
16
17#include <cassert>
18#include <sstream>
19#include <iomanip>
20#include <iostream>
21#include <cmath>
22
23#include "string_manip.h"
24#include "string_filter.h"
25
26#include "format_output.h"
27#include "profile_container.h"
28#include "callgraph_container.h"
29#include "diff_container.h"
30#include "arrange_profiles.h"
31#include "xml_output.h"
32#include "xml_utils.h"
33#include "cverb.h"
34
35using namespace std;
36
37namespace {
38
39
40string const get_linenr_info(file_location const floc, bool lf)
41{
42	ostringstream out;
43
44	string const & filename = lf
45		? debug_names.name(floc.filename)
46		: debug_names.basename(floc.filename);
47
48	if (!filename.empty())
49		out << filename << ":" << floc.linenr;
50	else
51		out << "(no location information)";
52
53	return out.str();
54}
55
56string get_vma(bfd_vma vma, bool vma_64)
57{
58	ostringstream out;
59	int width = vma_64 ? 16 : 8;
60
61	out << hex << setw(width) << setfill('0') << vma;
62
63	return out.str();
64}
65
66string get_percent(count_type dividend, count_type divisor)
67{
68	double ratio = op_ratio(dividend, divisor);
69
70	return ::format_percent(ratio * 100, percent_int_width,
71	                     percent_fract_width);
72}
73
74bool extract_linenr_info(string const & info, string & file, size_t & line)
75{
76	line = 0;
77	file = "";
78	string::size_type colon_pos = info.find(":");
79
80	if (colon_pos == string::npos)
81		return false;
82
83	file = info.substr(0, colon_pos);
84	istringstream is_info(info.substr(colon_pos+1));
85	is_info >> line;
86	return true;
87}
88
89
90} // anonymous namespace
91
92namespace format_output {
93
94formatter::formatter(extra_images const & extra)
95	:
96	nr_classes(1),
97	flags(ff_none),
98	vma_64(false),
99	long_filenames(false),
100	need_header(true),
101	extra_found_images(extra)
102{
103	format_map[ff_vma] = field_description(9, "vma", &formatter::format_vma);
104	format_map[ff_nr_samples] = field_description(9, "samples", &formatter::format_nr_samples);
105	format_map[ff_nr_samples_cumulated] = field_description(14, "cum. samples", &formatter::format_nr_cumulated_samples);
106	format_map[ff_percent] = field_description(9, "%", &formatter::format_percent);
107	format_map[ff_percent_cumulated] = field_description(11, "cum. %", &formatter::format_cumulated_percent);
108	format_map[ff_linenr_info] = field_description(28, "linenr info", &formatter::format_linenr_info);
109	format_map[ff_image_name] = field_description(25, "image name", &formatter::format_image_name);
110	format_map[ff_app_name] = field_description(25, "app name", &formatter::format_app_name);
111	format_map[ff_symb_name] = field_description(30, "symbol name", &formatter::format_symb_name);
112	format_map[ff_percent_details] = field_description(9, "%", &formatter::format_percent_details);
113	format_map[ff_percent_cumulated_details] = field_description(10, "cum. %", &formatter::format_cumulated_percent_details);
114	format_map[ff_diff] = field_description(10, "diff %", &formatter::format_diff);
115}
116
117
118formatter::~formatter()
119{
120}
121
122
123void formatter::set_nr_classes(size_t nr)
124{
125	nr_classes = nr;
126}
127
128
129void formatter::add_format(format_flags flag)
130{
131	flags = static_cast<format_flags>(flags | flag);
132}
133
134
135void formatter::show_header(bool on_off)
136{
137	need_header = on_off;
138}
139
140
141void formatter::vma_format_64bit(bool on_off)
142{
143	vma_64 = on_off;
144}
145
146
147void formatter::show_long_filenames(bool on_off)
148{
149	long_filenames = on_off;
150}
151
152
153void formatter::show_global_percent(bool on_off)
154{
155	global_percent = on_off;
156}
157
158
159void formatter::output_header(ostream & out)
160{
161	if (!need_header)
162		return;
163
164	size_t padding = 0;
165
166	// first output the vma field
167	if (flags & ff_vma)
168		padding = output_header_field(out, ff_vma, padding);
169
170	// the field repeated for each profile class
171	for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
172		if (flags & ff_nr_samples)
173			padding = output_header_field(out,
174			      ff_nr_samples, padding);
175
176		if (flags & ff_nr_samples_cumulated)
177			padding = output_header_field(out,
178			       ff_nr_samples_cumulated, padding);
179
180		if (flags & ff_percent)
181			padding = output_header_field(out,
182			       ff_percent, padding);
183
184		if (flags & ff_percent_cumulated)
185			padding = output_header_field(out,
186			       ff_percent_cumulated, padding);
187
188		if (flags & ff_diff)
189			padding = output_header_field(out,
190				ff_diff, padding);
191
192		if (flags & ff_percent_details)
193			padding = output_header_field(out,
194			       ff_percent_details, padding);
195
196		if (flags & ff_percent_cumulated_details)
197			padding = output_header_field(out,
198			       ff_percent_cumulated_details, padding);
199	}
200
201	// now the remaining field
202	if (flags & ff_linenr_info)
203		padding = output_header_field(out, ff_linenr_info, padding);
204
205	if (flags & ff_image_name)
206		padding = output_header_field(out, ff_image_name, padding);
207
208	if (flags & ff_app_name)
209		padding = output_header_field(out, ff_app_name, padding);
210
211	if (flags & ff_symb_name)
212		padding = output_header_field(out, ff_symb_name, padding);
213
214	out << "\n";
215}
216
217
218/// describe each possible field of colummned output.
219// FIXME: use % of the screen width here. sum of % equal to 100, then calculate
220// ratio between 100 and the selected % to grow non fixed field use also
221// lib[n?]curses to get the console width (look info source) (so on add a fixed
222// field flags)
223size_t formatter::
224output_field(ostream & out, field_datum const & datum,
225             format_flags fl, size_t padding, bool hide_immutable)
226{
227	if (!hide_immutable) {
228		out << string(padding, ' ');
229
230		field_description const & field(format_map[fl]);
231		string str = (this->*field.formatter)(datum);
232		out << str;
233
234		// at least one separator char
235		padding = 1;
236		if (str.length() < field.width)
237			padding = field.width - str.length();
238	} else {
239		field_description const & field(format_map[fl]);
240		padding += field.width;
241	}
242
243	return padding;
244}
245
246
247size_t formatter::
248output_header_field(ostream & out, format_flags fl, size_t padding)
249{
250	out << string(padding, ' ');
251
252	field_description const & field(format_map[fl]);
253	out << field.header_name;
254
255	// at least one separator char
256	padding = 1;
257	if (field.header_name.length() < field.width)
258		padding = field.width - field.header_name.length();
259
260	return padding;
261}
262
263
264string formatter::format_vma(field_datum const & f)
265{
266	return get_vma(f.sample.vma, vma_64);
267}
268
269
270string formatter::format_symb_name(field_datum const & f)
271{
272	return symbol_names.demangle(f.symbol.name);
273}
274
275
276string formatter::format_image_name(field_datum const & f)
277{
278	return get_image_name(f.symbol.image_name,
279		long_filenames
280			? image_name_storage::int_real_filename
281			: image_name_storage::int_real_basename,
282		extra_found_images);
283}
284
285
286string formatter::format_app_name(field_datum const & f)
287{
288	return get_image_name(f.symbol.app_name,
289		long_filenames
290			? image_name_storage::int_real_filename
291			: image_name_storage::int_real_basename,
292		extra_found_images);
293}
294
295
296string formatter::format_linenr_info(field_datum const & f)
297{
298	return get_linenr_info(f.sample.file_loc, long_filenames);
299}
300
301
302string formatter::format_nr_samples(field_datum const & f)
303{
304	ostringstream out;
305	out << f.sample.counts[f.pclass];
306	return out.str();
307}
308
309
310string formatter::format_nr_cumulated_samples(field_datum const & f)
311{
312	if (f.diff == -INFINITY)
313		return "---";
314	ostringstream out;
315	f.counts.cumulated_samples[f.pclass] += f.sample.counts[f.pclass];
316	out << f.counts.cumulated_samples[f.pclass];
317	return out.str();
318}
319
320
321string formatter::format_percent(field_datum const & f)
322{
323	if (f.diff == -INFINITY)
324		return "---";
325	return get_percent(f.sample.counts[f.pclass], f.counts.total[f.pclass]);
326}
327
328
329string formatter::format_cumulated_percent(field_datum const & f)
330{
331	if (f.diff == -INFINITY)
332		return "---";
333	f.counts.cumulated_percent[f.pclass] += f.sample.counts[f.pclass];
334
335	return get_percent(f.counts.cumulated_percent[f.pclass],
336	                   f.counts.total[f.pclass]);
337}
338
339
340string formatter::format_percent_details(field_datum const & f)
341{
342	return get_percent(f.sample.counts[f.pclass],
343		f.counts.total[f.pclass]);
344}
345
346
347string formatter::format_cumulated_percent_details(field_datum const & f)
348{
349	f.counts.cumulated_percent_details[f.pclass] += f.sample.counts[f.pclass];
350
351	return get_percent(f.counts.cumulated_percent_details[f.pclass],
352	                   f.counts.total[f.pclass]);
353}
354
355
356string formatter::format_diff(field_datum const & f)
357{
358	if (f.diff == INFINITY)
359		return "+++";
360	else if (f.diff == -INFINITY)
361		return "---";
362
363	return ::format_percent(f.diff, percent_int_width,
364                                percent_fract_width, true);
365}
366
367
368void formatter::
369do_output(ostream & out, symbol_entry const & symb, sample_entry const & sample,
370          counts_t & c, diff_array_t const & diffs, bool hide_immutable)
371{
372	size_t padding = 0;
373
374	// first output the vma field
375	field_datum datum(symb, sample, 0, c, extra_found_images);
376	if (flags & ff_vma)
377		padding = output_field(out, datum, ff_vma, padding, false);
378
379	// repeated fields for each profile class
380	for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
381		field_datum datum(symb, sample, pclass, c,
382				  extra_found_images, diffs[pclass]);
383
384		if (flags & ff_nr_samples)
385			padding = output_field(out, datum,
386			       ff_nr_samples, padding, false);
387
388		if (flags & ff_nr_samples_cumulated)
389			padding = output_field(out, datum,
390			       ff_nr_samples_cumulated, padding, false);
391
392		if (flags & ff_percent)
393			padding = output_field(out, datum,
394			       ff_percent, padding, false);
395
396		if (flags & ff_percent_cumulated)
397			padding = output_field(out, datum,
398			       ff_percent_cumulated, padding, false);
399
400		if (flags & ff_diff)
401			padding = output_field(out, datum,
402				ff_diff, padding, false);
403
404		if (flags & ff_percent_details)
405			padding = output_field(out, datum,
406			       ff_percent_details, padding, false);
407
408		if (flags & ff_percent_cumulated_details)
409			padding = output_field(out, datum,
410			       ff_percent_cumulated_details, padding, false);
411	}
412
413	// now the remaining field
414	if (flags & ff_linenr_info)
415		padding = output_field(out, datum, ff_linenr_info,
416		       padding, false);
417
418	if (flags & ff_image_name)
419		padding = output_field(out, datum, ff_image_name,
420		       padding, hide_immutable);
421
422	if (flags & ff_app_name)
423		padding = output_field(out, datum, ff_app_name,
424		       padding, hide_immutable);
425
426	if (flags & ff_symb_name)
427		padding = output_field(out, datum, ff_symb_name,
428		       padding, hide_immutable);
429
430	out << "\n";
431}
432
433
434opreport_formatter::opreport_formatter(profile_container const & p)
435	:
436	formatter(p.extra_found_images),
437	profile(p),
438	need_details(false)
439{
440	counts.total = profile.samples_count();
441}
442
443
444void opreport_formatter::show_details(bool on_off)
445{
446	need_details = on_off;
447}
448
449
450void opreport_formatter::output(ostream & out, symbol_entry const * symb)
451{
452	do_output(out, *symb, symb->sample, counts);
453
454	if (need_details)
455		output_details(out, symb);
456}
457
458
459void opreport_formatter::
460output(ostream & out, symbol_collection const & syms)
461{
462	output_header(out);
463
464	symbol_collection::const_iterator it = syms.begin();
465	symbol_collection::const_iterator end = syms.end();
466	for (; it != end; ++it)
467		output(out, *it);
468}
469
470
471void opreport_formatter::
472output_details(ostream & out, symbol_entry const * symb)
473{
474	counts_t c = counts;
475
476	if (!global_percent)
477		c.total = symb->sample.counts;
478
479	// cumulated percent are relative to current symbol.
480	c.cumulated_samples = count_array_t();
481	c.cumulated_percent = count_array_t();
482
483	sample_container::samples_iterator it = profile.begin(symb);
484	sample_container::samples_iterator end = profile.end(symb);
485	for (; it != end; ++it) {
486		out << "  ";
487		do_output(out, *symb, it->second, c, diff_array_t(), true);
488	}
489}
490
491
492cg_formatter::cg_formatter(callgraph_container const & profile)
493	:
494	formatter(profile.extra_found_images)
495{
496	counts.total = profile.samples_count();
497}
498
499
500void cg_formatter::output(ostream & out, symbol_collection const & syms)
501{
502	// amount of spacing prefixing child and parent lines
503	string const child_parent_prefix("  ");
504
505	output_header(out);
506
507	out << string(79, '-') << endl;
508
509	symbol_collection::const_iterator it;
510	symbol_collection::const_iterator end = syms.end();
511
512	for (it = syms.begin(); it < end; ++it) {
513		cg_symbol const * sym = dynamic_cast<cg_symbol const *>(*it);
514
515		cg_symbol::children::const_iterator cit;
516		cg_symbol::children::const_iterator cend = sym->callers.end();
517
518		counts_t c;
519		if (global_percent)
520			c.total = counts.total;
521		else
522			c.total = sym->total_caller_count;
523
524		for (cit = sym->callers.begin(); cit != cend; ++cit) {
525			out << child_parent_prefix;
526			do_output(out, *cit, cit->sample, c);
527		}
528
529		do_output(out, *sym, sym->sample, counts);
530
531		c = counts_t();
532		if (global_percent)
533			c.total = counts.total;
534		else
535			c.total = sym->total_callee_count;
536
537		cend = sym->callees.end();
538
539		for (cit = sym->callees.begin(); cit != cend; ++cit) {
540			out << child_parent_prefix;
541			do_output(out, *cit, cit->sample, c);
542		}
543
544		out << string(79, '-') << endl;
545	}
546}
547
548
549diff_formatter::diff_formatter(diff_container const & profile,
550			       extra_images const & extra)
551	:
552	formatter(extra)
553{
554	counts.total = profile.samples_count();
555}
556
557
558void diff_formatter::output(ostream & out, diff_collection const & syms)
559{
560	output_header(out);
561
562	diff_collection::const_iterator it = syms.begin();
563	diff_collection::const_iterator end = syms.end();
564	for (; it != end; ++it)
565		do_output(out, *it, it->sample, counts, it->diffs);
566}
567
568// local variables used in generation of XML
569// buffer details for output later
570ostringstream bytes_out;
571
572// module+symbol table for detecting duplicate symbols
573map<string, size_t> symbol_data_table;
574size_t symbol_data_index = 0;
575
576/* Return any existing index or add to the table */
577size_t xml_get_symbol_index(string const & name)
578{
579	size_t index = symbol_data_index;
580	map<string, size_t>::iterator it = symbol_data_table.find(name);
581
582	if (it == symbol_data_table.end()) {
583		symbol_data_table[name] = symbol_data_index++;
584		return index;
585	}
586
587	return it->second;
588}
589
590
591class symbol_details_t {
592public:
593	symbol_details_t() { size = index = 0; id = -1; }
594	int id;
595	size_t size;
596	size_t index;
597	string details;
598};
599
600typedef growable_vector<symbol_details_t> symbol_details_array_t;
601symbol_details_array_t symbol_details;
602size_t detail_table_index = 0;
603
604xml_formatter::
605xml_formatter(profile_container const * p,
606	      symbol_collection & s, extra_images const & extra,
607	      string_filter const & sf)
608	:
609	formatter(extra),
610	profile(p),
611	symbols(s),
612	need_details(false),
613	symbol_filter(sf)
614{
615	if (profile)
616		counts.total = profile->samples_count();
617}
618
619
620void xml_formatter::
621show_details(bool on_off)
622{
623	need_details = on_off;
624}
625
626
627void xml_formatter::output(ostream & out)
628{
629	xml_support->build_subclasses(out);
630
631	xml_support->output_program_structure(out);
632	output_symbol_data(out);
633	if (need_details) {
634		out << open_element(DETAIL_TABLE);
635		for (size_t i = 0; i < symbol_details.size(); ++i) {
636			int id = symbol_details[i].id;
637
638			if (id >= 0) {
639				out << open_element(SYMBOL_DETAILS, true);
640				out << init_attr(TABLE_ID, (size_t)id);
641				out << close_element(NONE, true);
642				out << symbol_details[i].details;
643				out << close_element(SYMBOL_DETAILS);
644			}
645		}
646		out << close_element(DETAIL_TABLE);
647
648		// output bytesTable
649		out << open_element(BYTES_TABLE);
650		out << bytes_out.str();
651		out << close_element(BYTES_TABLE);
652	}
653
654	out << close_element(PROFILE);
655}
656
657bool
658xml_formatter::get_bfd_object(symbol_entry const * symb, op_bfd * & abfd) const
659{
660	bool ok = true;
661
662	string const & image_name = get_image_name(symb->image_name,
663		image_name_storage::int_filename, extra_found_images);
664	if (symb->spu_offset) {
665		// FIXME: what about archive:tmp, actually it's not supported
666		// for spu since oparchive doesn't archive the real file but
667		// in future it would work ?
668		string tmp = get_image_name(symb->embedding_filename,
669			image_name_storage::int_filename, extra_found_images);
670		if (abfd && abfd->get_filename() == tmp)
671			return true;
672		delete abfd;
673		abfd = new op_bfd(symb->spu_offset, tmp,
674				  symbol_filter, extra_found_images, ok);
675	} else {
676		if (abfd && abfd->get_filename() == image_name)
677			return true;
678		delete abfd;
679		abfd = new op_bfd(image_name, symbol_filter,
680				  extra_found_images, ok);
681
682	}
683
684	if (!ok) {
685		report_image_error(image_name, image_format_failure,
686				   false, extra_found_images);
687		delete abfd;
688		abfd = 0;
689		return false;
690	}
691
692	return true;
693}
694
695void xml_formatter::
696output_the_symbol_data(ostream & out, symbol_entry const * symb, op_bfd * & abfd)
697{
698	string const name = symbol_names.name(symb->name);
699	assert(name.size() > 0);
700
701	string const image = get_image_name(symb->image_name,
702		image_name_storage::int_filename, extra_found_images);
703	string const qname = image + ":" + name;
704	map<string, size_t>::iterator sd_it = symbol_data_table.find(qname);
705
706	if (sd_it != symbol_data_table.end()) {
707		// first time we've seen this symbol
708		out << open_element(SYMBOL_DATA, true);
709		out << init_attr(TABLE_ID, sd_it->second);
710
711		field_datum datum(*symb, symb->sample, 0, counts,
712				  extra_found_images);
713
714		output_attribute(out, datum, ff_symb_name, NAME);
715
716		if (flags & ff_linenr_info) {
717			output_attribute(out, datum, ff_linenr_info, SOURCE_FILE);
718			output_attribute(out, datum, ff_linenr_info, SOURCE_LINE);
719		}
720
721		if (name.size() > 0 && name[0] != '?') {
722			output_attribute(out, datum, ff_vma, STARTING_ADDR);
723
724			if (need_details) {
725				get_bfd_object(symb, abfd);
726				if (abfd && abfd->symbol_has_contents(symb->sym_index))
727					xml_support->output_symbol_bytes(bytes_out, symb, sd_it->second, *abfd);
728			}
729		}
730		out << close_element();
731
732		// seen so remove (otherwise get several "no symbols")
733		symbol_data_table.erase(qname);
734	}
735}
736
737void xml_formatter::output_cg_children(ostream & out,
738	cg_symbol::children const cg_symb, op_bfd * & abfd)
739{
740	cg_symbol::children::const_iterator cit;
741	cg_symbol::children::const_iterator cend = cg_symb.end();
742
743	for (cit = cg_symb.begin(); cit != cend; ++cit) {
744		string const name = symbol_names.name(cit->name);
745		string const image = get_image_name(cit->image_name,
746			image_name_storage::int_filename, extra_found_images);
747		string const qname = image + ":" + name;
748		map<string, size_t>::iterator sd_it = symbol_data_table.find(qname);
749
750		if (sd_it != symbol_data_table.end()) {
751			symbol_entry const * child = &(*cit);
752			output_the_symbol_data(out, child, abfd);
753		}
754	}
755}
756
757void xml_formatter::output_symbol_data(ostream & out)
758{
759	op_bfd * abfd = NULL;
760	sym_iterator it = symbols.begin();
761	sym_iterator end = symbols.end();
762
763	out << open_element(SYMBOL_TABLE);
764	for ( ; it != end; ++it) {
765		symbol_entry const * symb = *it;
766		cg_symbol const * cg_symb = dynamic_cast<cg_symbol const *>(symb);
767		output_the_symbol_data(out, symb, abfd);
768		if (cg_symb) {
769			/* make sure callers/callees are included in SYMBOL_TABLE */
770			output_cg_children(out, cg_symb->callers, abfd);
771			output_cg_children(out, cg_symb->callees, abfd);
772		}
773	}
774	out << close_element(SYMBOL_TABLE);
775
776	delete abfd;
777}
778
779string  xml_formatter::
780output_symbol_details(symbol_entry const * symb,
781    size_t & detail_index, size_t const lo, size_t const hi)
782{
783	if (!has_sample_counts(symb->sample.counts, lo, hi))
784		return "";
785
786	sample_container::samples_iterator it = profile->begin(symb);
787	sample_container::samples_iterator end = profile->end(symb);
788
789	ostringstream str;
790	for (; it != end; ++it) {
791		counts_t c;
792
793		for (size_t p = lo; p <= hi; ++p)  {
794			size_t count = it->second.counts[p];
795
796			if (count == 0) continue;
797
798			str << open_element(DETAIL_DATA, true);
799			str << init_attr(TABLE_ID, detail_index++);
800
801			// first output the vma field
802			field_datum datum(*symb, it->second, 0, c,
803					  extra_found_images, 0.0);
804			output_attribute(str, datum, ff_vma, VMA);
805			if (ff_linenr_info) {
806				string sym_file;
807				size_t sym_line;
808				string samp_file;
809				size_t samp_line;
810				string sym_info = get_linenr_info(symb->sample.file_loc, true);
811				string samp_info = get_linenr_info(it->second.file_loc, true);
812
813				if (extract_linenr_info(samp_info, samp_file, samp_line)) {
814					if (extract_linenr_info(sym_info, sym_file, sym_line)) {
815						// only output source_file if it is different than the symbol's
816						// source file.  this can happen with inlined functions in
817						// #included header files
818						if (sym_file != samp_file)
819							str << init_attr(SOURCE_FILE, samp_file);
820					}
821					str << init_attr(SOURCE_LINE, samp_line);
822				}
823			}
824			str << close_element(NONE, true);
825
826			// output buffered sample data
827			output_sample_data(str, it->second, p);
828
829			str << close_element(DETAIL_DATA);
830		}
831	}
832	return str.str();
833}
834
835void xml_formatter::
836output_symbol(ostream & out,
837	symbol_entry const * symb, size_t lo, size_t hi, bool is_module)
838{
839	ostringstream str;
840	// pointless reference to is_module, remove insane compiler warning
841	size_t indx = is_module ? 0 : 1;
842
843	// output symbol's summary data for each profile class
844	bool got_samples = false;
845
846	for (size_t p = lo; p <= hi; ++p) {
847		got_samples |= xml_support->output_summary_data(str,
848		    symb->sample.counts, p);
849	}
850
851	if (!got_samples)
852		return;
853
854	if (cverb << vxml)
855		out << "<!-- symbol_ref=" << symbol_names.name(symb->name) <<
856			" -->" << endl;
857
858	out << open_element(SYMBOL, true);
859
860	string const name = symbol_names.name(symb->name);
861	assert(name.size() > 0);
862
863	string const image = get_image_name(symb->image_name,
864		image_name_storage::int_filename, extra_found_images);
865	string const qname = image + ":" + name;
866
867	indx = xml_get_symbol_index(qname);
868
869	out << init_attr(ID_REF, indx);
870
871	if (need_details) {
872		ostringstream details;
873		symbol_details_t & sd = symbol_details[indx];
874		size_t const detail_lo = sd.index;
875
876		string detail_str = output_symbol_details(symb, sd.index, lo, hi);
877
878		if (detail_str.size() > 0) {
879			if (sd.id < 0)
880				sd.id = indx;
881			details << detail_str;
882		}
883
884		if (sd.index > detail_lo) {
885			sd.details = sd.details + details.str();
886			out << init_attr(DETAIL_LO, detail_lo);
887			out << init_attr(DETAIL_HI, sd.index-1);
888		}
889	}
890	out << close_element(NONE, true);
891	// output summary
892	out << str.str();
893	out << close_element(SYMBOL);
894}
895
896
897void xml_formatter::
898output_sample_data(ostream & out, sample_entry const & sample, size_t pclass)
899{
900	out << open_element(COUNT, true);
901	out << init_attr(CLASS, classes.v[pclass].name);
902	out << close_element(NONE, true);
903	out << sample.counts[pclass];
904	out << close_element(COUNT);
905}
906
907
908void xml_formatter::
909output_attribute(ostream & out, field_datum const & datum,
910                 format_flags fl, tag_t tag)
911{
912	field_description const & field(format_map[fl]);
913
914	string str = (this->*field.formatter)(datum);
915
916	if (!str.empty()) {
917		if (fl == ff_linenr_info && (tag == SOURCE_LINE || tag == SOURCE_FILE)) {
918			string file;
919			size_t line;
920
921			if (extract_linenr_info(str, file, line)) {
922				if (tag == SOURCE_LINE)
923					out << init_attr(tag, line);
924				else
925					out << init_attr(tag, file);
926			}
927		} else
928			out << " " << init_attr(tag, str);
929	}
930}
931
932xml_cg_formatter::
933xml_cg_formatter(callgraph_container const & cg, symbol_collection & s,
934		 string_filter const & sf)
935	:
936	xml_formatter(0, s, cg.extra_found_images, sf),
937	callgraph(cg)
938{
939	counts.total = callgraph.samples_count();
940}
941
942void xml_cg_formatter::
943output_symbol_core(ostream & out, cg_symbol::children const cg_symb,
944       string const selfname, string const qname,
945       size_t lo, size_t hi, bool is_module, tag_t tag)
946{
947	cg_symbol::children::const_iterator cit;
948	cg_symbol::children::const_iterator cend = cg_symb.end();
949
950	for (cit = cg_symb.begin(); cit != cend; ++cit) {
951		string const & module = get_image_name((cit)->image_name,
952			image_name_storage::int_filename, extra_found_images);
953		bool self = false;
954		ostringstream str;
955		size_t indx;
956
957		// output symbol's summary data for each profile class
958		for (size_t p = lo; p <= hi; ++p)
959			xml_support->output_summary_data(str, cit->sample.counts, p);
960
961		if (cverb << vxml)
962			out << "<!-- symbol_ref=" << symbol_names.name(cit->name) <<
963				" -->" << endl;
964
965		if (is_module) {
966			out << open_element(MODULE, true);
967			out << init_attr(NAME, module) << close_element(NONE, true);
968		}
969
970		out << open_element(SYMBOL, true);
971
972		string const symname = symbol_names.name(cit->name);
973		assert(symname.size() > 0);
974
975		string const symqname = module + ":" + symname;
976
977		// Find any self references and handle
978		if ((symname == selfname) && (tag == CALLEES)) {
979			self = true;
980			indx = xml_get_symbol_index(qname);
981		} else {
982			indx = xml_get_symbol_index(symqname);
983		}
984
985		out << init_attr(ID_REF, indx);
986
987		if (self)
988			out << init_attr(SELFREF, "true");
989
990		out << close_element(NONE, true);
991		out << str.str();
992		out << close_element(SYMBOL);
993
994		if (is_module)
995			out << close_element(MODULE);
996	}
997}
998
999
1000void xml_cg_formatter::
1001output_symbol(ostream & out,
1002	symbol_entry const * symb, size_t lo, size_t hi, bool is_module)
1003{
1004	cg_symbol const * cg_symb = dynamic_cast<cg_symbol const *>(symb);
1005	ostringstream str;
1006	size_t indx;
1007
1008	// output symbol's summary data for each profile class
1009	for (size_t p = lo; p <= hi; ++p)
1010		xml_support->output_summary_data(str, symb->sample.counts, p);
1011
1012	if (cverb << vxml)
1013		out << "<!-- symbol_ref=" << symbol_names.name(symb->name) <<
1014			" -->" << endl;
1015
1016	out << open_element(SYMBOL, true);
1017
1018	string const name = symbol_names.name(symb->name);
1019	assert(name.size() > 0);
1020
1021	string const image = get_image_name(symb->image_name,
1022		image_name_storage::int_filename, extra_found_images);
1023	string const qname = image + ":" + name;
1024
1025	string const selfname = symbol_names.demangle(symb->name) + " [self]";
1026
1027	indx = xml_get_symbol_index(qname);
1028
1029	out << init_attr(ID_REF, indx);
1030
1031	out << close_element(NONE, true);
1032
1033	out << open_element(CALLERS);
1034	if (cg_symb)
1035		output_symbol_core(out, cg_symb->callers, selfname, qname, lo, hi, is_module, CALLERS);
1036	out << close_element(CALLERS);
1037
1038	out << open_element(CALLEES);
1039	if (cg_symb)
1040		output_symbol_core(out, cg_symb->callees, selfname, qname, lo, hi, is_module, CALLEES);
1041
1042	out << close_element(CALLEES);
1043
1044	// output summary
1045	out << str.str();
1046	out << close_element(SYMBOL);
1047}
1048
1049} // namespace format_output
1050