~arosales/test/oprofile

3 by Antonio Rosales
Move files up one directory.
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
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
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
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
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
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
118
formatter::~formatter()
119
{
120
}
121
122
123
void formatter::set_nr_classes(size_t nr)
124
{
125
	nr_classes = nr;
126
}
127
128
129
void formatter::add_format(format_flags flag)
130
{
131
	flags = static_cast<format_flags>(flags | flag);
132
}
133
134
135
void formatter::show_header(bool on_off)
136
{
137
	need_header = on_off;
138
}
139
 
140
141
void formatter::vma_format_64bit(bool on_off)
142
{
143
	vma_64 = on_off;
144
}
145
146
147
void formatter::show_long_filenames(bool on_off)
148
{
149
	long_filenames = on_off;
150
}
151
152
153
void formatter::show_global_percent(bool on_off)
154
{
155
	global_percent = on_off;
156
}
157
158
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::
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::
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
264
string formatter::format_vma(field_datum const & f)
265
{
266
	return get_vma(f.sample.vma, vma_64);
267
}
268
269
 
270
string formatter::format_symb_name(field_datum const & f)
271
{
272
	return symbol_names.demangle(f.symbol.name);
273
}
274
275
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
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::
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
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
 
444
void opreport_formatter::show_details(bool on_off)
445
{
446
	need_details = on_off;
447
}
448
449
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::
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::
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
 
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
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
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
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 */
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:
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::
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::
621
show_details(bool on_off)
622
{
623
	need_details = on_off;
624
}
625
626
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
658
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::
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
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
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::
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::
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::
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::
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::
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::
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::
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