• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 using namespace std;
36 
37 namespace {
38 
39 
get_linenr_info(file_location const floc,bool lf)40 string 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 
get_vma(bfd_vma vma,bool vma_64)56 string 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 
get_percent(count_type dividend,count_type divisor)66 string 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 
extract_linenr_info(string const & info,string & file,size_t & line)74 bool 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 
92 namespace format_output {
93 
formatter(extra_images const & extra)94 formatter::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 
~formatter()118 formatter::~formatter()
119 {
120 }
121 
122 
set_nr_classes(size_t nr)123 void formatter::set_nr_classes(size_t nr)
124 {
125 	nr_classes = nr;
126 }
127 
128 
add_format(format_flags flag)129 void formatter::add_format(format_flags flag)
130 {
131 	flags = static_cast<format_flags>(flags | flag);
132 }
133 
134 
show_header(bool on_off)135 void formatter::show_header(bool on_off)
136 {
137 	need_header = on_off;
138 }
139 
140 
vma_format_64bit(bool on_off)141 void formatter::vma_format_64bit(bool on_off)
142 {
143 	vma_64 = on_off;
144 }
145 
146 
show_long_filenames(bool on_off)147 void formatter::show_long_filenames(bool on_off)
148 {
149 	long_filenames = on_off;
150 }
151 
152 
show_global_percent(bool on_off)153 void formatter::show_global_percent(bool on_off)
154 {
155 	global_percent = on_off;
156 }
157 
158 
output_header(ostream & out)159 void 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)
223 size_t formatter::
output_field(ostream & out,field_datum const & datum,format_flags fl,size_t padding,bool hide_immutable)224 output_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 
247 size_t formatter::
output_header_field(ostream & out,format_flags fl,size_t padding)248 output_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 
format_vma(field_datum const & f)264 string formatter::format_vma(field_datum const & f)
265 {
266 	return get_vma(f.sample.vma, vma_64);
267 }
268 
269 
format_symb_name(field_datum const & f)270 string formatter::format_symb_name(field_datum const & f)
271 {
272 	return symbol_names.demangle(f.symbol.name);
273 }
274 
275 
format_image_name(field_datum const & f)276 string 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 
format_app_name(field_datum const & f)286 string 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 
format_linenr_info(field_datum const & f)296 string formatter::format_linenr_info(field_datum const & f)
297 {
298 	return get_linenr_info(f.sample.file_loc, long_filenames);
299 }
300 
301 
format_nr_samples(field_datum const & f)302 string 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 
format_nr_cumulated_samples(field_datum const & f)310 string 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 
format_percent(field_datum const & f)321 string 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 
format_cumulated_percent(field_datum const & f)329 string 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 
format_percent_details(field_datum const & f)340 string 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 
format_cumulated_percent_details(field_datum const & f)347 string 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 
format_diff(field_datum const & f)356 string 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 
368 void formatter::
do_output(ostream & out,symbol_entry const & symb,sample_entry const & sample,counts_t & c,diff_array_t const & diffs,bool hide_immutable)369 do_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 
opreport_formatter(profile_container const & p)434 opreport_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 
show_details(bool on_off)444 void opreport_formatter::show_details(bool on_off)
445 {
446 	need_details = on_off;
447 }
448 
449 
output(ostream & out,symbol_entry const * symb)450 void 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 
459 void opreport_formatter::
output(ostream & out,symbol_collection const & syms)460 output(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 
471 void opreport_formatter::
output_details(ostream & out,symbol_entry const * symb)472 output_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 
cg_formatter(callgraph_container const & profile)492 cg_formatter::cg_formatter(callgraph_container const & profile)
493 	:
494 	formatter(profile.extra_found_images)
495 {
496 	counts.total = profile.samples_count();
497 }
498 
499 
output(ostream & out,symbol_collection const & syms)500 void 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 
diff_formatter(diff_container const & profile,extra_images const & extra)549 diff_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 
output(ostream & out,diff_collection const & syms)558 void 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
570 ostringstream bytes_out;
571 
572 // module+symbol table for detecting duplicate symbols
573 map<string, size_t> symbol_data_table;
574 size_t symbol_data_index = 0;
575 
576 /* Return any existing index or add to the table */
xml_get_symbol_index(string const & name)577 size_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 
591 class symbol_details_t {
592 public:
symbol_details_t()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 
600 typedef growable_vector<symbol_details_t> symbol_details_array_t;
601 symbol_details_array_t symbol_details;
602 size_t detail_table_index = 0;
603 
604 xml_formatter::
xml_formatter(profile_container const * p,symbol_collection & s,extra_images const & extra,string_filter const & sf)605 xml_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 
620 void xml_formatter::
show_details(bool on_off)621 show_details(bool on_off)
622 {
623 	need_details = on_off;
624 }
625 
626 
output(ostream & out)627 void 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 
657 bool
get_bfd_object(symbol_entry const * symb,op_bfd * & abfd) const658 xml_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 
695 void xml_formatter::
output_the_symbol_data(ostream & out,symbol_entry const * symb,op_bfd * & abfd)696 output_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 
output_cg_children(ostream & out,cg_symbol::children const cg_symb,op_bfd * & abfd)737 void 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 
output_symbol_data(ostream & out)757 void 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 
779 string  xml_formatter::
output_symbol_details(symbol_entry const * symb,size_t & detail_index,size_t const lo,size_t const hi)780 output_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 
835 void xml_formatter::
output_symbol(ostream & out,symbol_entry const * symb,size_t lo,size_t hi,bool is_module)836 output_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 
897 void xml_formatter::
output_sample_data(ostream & out,sample_entry const & sample,size_t pclass)898 output_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 
908 void xml_formatter::
output_attribute(ostream & out,field_datum const & datum,format_flags fl,tag_t tag)909 output_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 
932 xml_cg_formatter::
xml_cg_formatter(callgraph_container const & cg,symbol_collection & s,string_filter const & sf)933 xml_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 
942 void xml_cg_formatter::
output_symbol_core(ostream & out,cg_symbol::children const cg_symb,string const selfname,string const qname,size_t lo,size_t hi,bool is_module,tag_t tag)943 output_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 
1000 void xml_cg_formatter::
output_symbol(ostream & out,symbol_entry const * symb,size_t lo,size_t hi,bool is_module)1001 output_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