format_output.cpp revision cc2ee177dbb3befca43e36cfc56778b006c3d050
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 <sstream>
18#include <iomanip>
19#include <iostream>
20#include <cmath>
21
22#include "string_manip.h"
23
24#include "format_output.h"
25#include "profile_container.h"
26#include "callgraph_container.h"
27#include "diff_container.h"
28
29using namespace std;
30
31
32namespace {
33
34string const & get_image_name(image_name_id id, bool lf)
35{
36	return lf ? image_names.name(id) : image_names.basename(id);
37}
38
39string const get_linenr_info(file_location const floc, bool lf)
40{
41	ostringstream out;
42
43	string const & filename = lf
44		? debug_names.name(floc.filename)
45		: debug_names.basename(floc.filename);
46
47	if (!filename.empty()) {
48		out << filename << ":" << floc.linenr;
49	} else {
50		out << "(no location information)";
51	}
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(size_t dividend, size_t 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} // anonymous namespace
75
76
77namespace format_output {
78
79
80formatter::formatter()
81	:
82	nr_classes(1),
83	flags(ff_none),
84	vma_64(false),
85	long_filenames(false),
86	need_header(true)
87{
88	format_map[ff_vma] = field_description(9, "vma", &formatter::format_vma);
89	format_map[ff_nr_samples] = field_description(9, "samples", &formatter::format_nr_samples);
90	format_map[ff_nr_samples_cumulated] = field_description(14, "cum. samples", &formatter::format_nr_cumulated_samples);
91	format_map[ff_percent] = field_description(9, "%", &formatter::format_percent);
92	format_map[ff_percent_cumulated] = field_description(11, "cum. %", &formatter::format_cumulated_percent);
93	format_map[ff_linenr_info] = field_description(28, "linenr info", &formatter::format_linenr_info);
94	format_map[ff_image_name] = field_description(25, "image name", &formatter::format_image_name);
95	format_map[ff_app_name] = field_description(25, "app name", &formatter::format_app_name);
96	format_map[ff_symb_name] = field_description(30, "symbol name", &formatter::format_symb_name);
97	format_map[ff_percent_details] = field_description(9, "%", &formatter::format_percent_details);
98	format_map[ff_percent_cumulated_details] = field_description(10, "cum. %", &formatter::format_cumulated_percent_details);
99	format_map[ff_diff] = field_description(10, "diff %", &formatter::format_diff);
100}
101
102
103formatter::~formatter()
104{
105}
106
107
108void formatter::set_nr_classes(size_t nr)
109{
110	nr_classes = nr;
111}
112
113
114void formatter::add_format(format_flags flag)
115{
116	flags = static_cast<format_flags>(flags | flag);
117}
118
119
120void formatter::show_header(bool on_off)
121{
122	need_header = on_off;
123}
124
125
126void formatter::vma_format_64bit(bool on_off)
127{
128	vma_64 = on_off;
129}
130
131
132void formatter::show_long_filenames(bool on_off)
133{
134	long_filenames = on_off;
135}
136
137
138void formatter::show_global_percent(bool on_off)
139{
140	global_percent = on_off;
141}
142
143
144void formatter::output_header(ostream & out)
145{
146	if (!need_header)
147		return;
148
149	size_t padding = 0;
150
151	// first output the vma field
152	if (flags & ff_vma)
153		padding = output_header_field(out, ff_vma, padding);
154
155	// the field repeated for each profile class
156	for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
157		if (flags & ff_nr_samples)
158			padding = output_header_field(out,
159			      ff_nr_samples, padding);
160
161		if (flags & ff_nr_samples_cumulated)
162			padding = output_header_field(out,
163			       ff_nr_samples_cumulated, padding);
164
165		if (flags & ff_percent)
166			padding = output_header_field(out,
167			       ff_percent, padding);
168
169		if (flags & ff_percent_cumulated)
170			padding = output_header_field(out,
171			       ff_percent_cumulated, padding);
172
173		if (flags & ff_diff)
174			padding = output_header_field(out,
175				ff_diff, padding);
176
177		if (flags & ff_percent_details)
178			padding = output_header_field(out,
179			       ff_percent_details, padding);
180
181		if (flags & ff_percent_cumulated_details)
182			padding = output_header_field(out,
183			       ff_percent_cumulated_details, padding);
184	}
185
186	// now the remaining field
187	if (flags & ff_linenr_info)
188		padding = output_header_field(out, ff_linenr_info, padding);
189
190	if (flags & ff_image_name)
191		padding = output_header_field(out, ff_image_name, padding);
192
193	if (flags & ff_app_name)
194		padding = output_header_field(out, ff_app_name, padding);
195
196	if (flags & ff_symb_name)
197		padding = output_header_field(out, ff_symb_name, padding);
198
199	out << "\n";
200}
201
202
203/// describe each possible field of colummned output.
204// FIXME: use % of the screen width here. sum of % equal to 100, then calculate
205// ratio between 100 and the selected % to grow non fixed field use also
206// lib[n?]curses to get the console width (look info source) (so on add a fixed
207// field flags)
208size_t formatter::
209output_field(ostream & out, field_datum const & datum,
210             format_flags fl, size_t padding, bool hide_immutable)
211{
212	if (!hide_immutable) {
213		out << string(padding, ' ');
214
215		field_description const & field(format_map[fl]);
216		string str = (this->*field.formatter)(datum);
217		out << str;
218
219		// at least one separator char
220		padding = 1;
221		if (str.length() < field.width)
222			padding = field.width - str.length();
223	} else {
224		field_description const & field(format_map[fl]);
225		padding += field.width;
226	}
227
228	return padding;
229}
230
231
232size_t formatter::
233output_header_field(ostream & out, format_flags fl, size_t padding)
234{
235	out << string(padding, ' ');
236
237	field_description const & field(format_map[fl]);
238	out << field.header_name;
239
240	// at least one separator char
241	padding = 1;
242	if (field.header_name.length() < field.width)
243		padding = field.width - field.header_name.length();
244
245	return padding;
246}
247
248
249string formatter::format_vma(field_datum const & f)
250{
251	return get_vma(f.sample.vma, vma_64);
252}
253
254
255string formatter::format_symb_name(field_datum const & f)
256{
257	return symbol_names.demangle(f.symbol.name);
258}
259
260
261string formatter::format_image_name(field_datum const & f)
262{
263	return get_image_name(f.symbol.image_name, long_filenames);
264}
265
266
267string formatter::format_app_name(field_datum const & f)
268{
269	return get_image_name(f.symbol.app_name, long_filenames);
270}
271
272
273string formatter::format_linenr_info(field_datum const & f)
274{
275	return get_linenr_info(f.sample.file_loc, long_filenames);
276}
277
278
279string formatter::format_nr_samples(field_datum const & f)
280{
281	ostringstream out;
282	out << f.sample.counts[f.pclass];
283	return out.str();
284}
285
286
287string formatter::format_nr_cumulated_samples(field_datum const & f)
288{
289	if (f.diff == -INFINITY)
290		return "---";
291	ostringstream out;
292	f.counts.cumulated_samples[f.pclass] += f.sample.counts[f.pclass];
293	out << f.counts.cumulated_samples[f.pclass];
294	return out.str();
295}
296
297
298string formatter::format_percent(field_datum const & f)
299{
300	if (f.diff == -INFINITY)
301		return "---";
302	return get_percent(f.sample.counts[f.pclass], f.counts.total[f.pclass]);
303}
304
305
306string formatter::format_cumulated_percent(field_datum const & f)
307{
308	if (f.diff == -INFINITY)
309		return "---";
310	f.counts.cumulated_percent[f.pclass] += f.sample.counts[f.pclass];
311
312	return get_percent(f.counts.cumulated_percent[f.pclass],
313	                   f.counts.total[f.pclass]);
314}
315
316
317string formatter::format_percent_details(field_datum const & f)
318{
319	return get_percent(f.sample.counts[f.pclass],
320		f.counts.total[f.pclass]);
321}
322
323
324string formatter::format_cumulated_percent_details(field_datum const & f)
325{
326	f.counts.cumulated_percent_details[f.pclass] += f.sample.counts[f.pclass];
327
328	return get_percent(f.counts.cumulated_percent_details[f.pclass],
329	                   f.counts.total[f.pclass]);
330}
331
332
333string formatter::format_diff(field_datum const & f)
334{
335	if (f.diff == INFINITY) {
336		ostringstream out;
337		out << "+++";
338		return out.str();
339	} else if (f.diff == -INFINITY) {
340		ostringstream out;
341		out << "---";
342		return out.str();
343	}
344
345	return ::format_percent(f.diff, percent_int_width,
346                                percent_fract_width, true);
347}
348
349
350void formatter::
351do_output(ostream & out, symbol_entry const & symb, sample_entry const & sample,
352          counts_t & c, diff_array_t const & diffs, bool hide_immutable)
353{
354	size_t padding = 0;
355
356	// first output the vma field
357	field_datum datum(symb, sample, 0, c);
358	if (flags & ff_vma)
359		padding = output_field(out, datum, ff_vma, padding, false);
360
361	// repeated fields for each profile class
362	for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
363		field_datum datum(symb, sample, pclass, c, diffs[pclass]);
364
365		if (flags & ff_nr_samples)
366			padding = output_field(out, datum,
367			       ff_nr_samples, padding, false);
368
369		if (flags & ff_nr_samples_cumulated)
370			padding = output_field(out, datum,
371			       ff_nr_samples_cumulated, padding, false);
372
373		if (flags & ff_percent)
374			padding = output_field(out, datum,
375			       ff_percent, padding, false);
376
377		if (flags & ff_percent_cumulated)
378			padding = output_field(out, datum,
379			       ff_percent_cumulated, padding, false);
380
381		if (flags & ff_diff)
382			padding = output_field(out, datum,
383				ff_diff, padding, false);
384
385		if (flags & ff_percent_details)
386			padding = output_field(out, datum,
387			       ff_percent_details, padding, false);
388
389		if (flags & ff_percent_cumulated_details)
390			padding = output_field(out, datum,
391			       ff_percent_cumulated_details, padding, false);
392	}
393
394	// now the remaining field
395	if (flags & ff_linenr_info)
396		padding = output_field(out, datum, ff_linenr_info,
397		       padding, false);
398
399	if (flags & ff_image_name)
400		padding = output_field(out, datum, ff_image_name,
401		       padding, hide_immutable);
402
403	if (flags & ff_app_name)
404		padding = output_field(out, datum, ff_app_name,
405		       padding, hide_immutable);
406
407	if (flags & ff_symb_name)
408		padding = output_field(out, datum, ff_symb_name,
409		       padding, hide_immutable);
410
411	out << "\n";
412}
413
414
415opreport_formatter::opreport_formatter(profile_container const & p)
416	:
417	profile(p),
418	need_details(false)
419{
420	counts.total = profile.samples_count();
421}
422
423
424void opreport_formatter::show_details(bool on_off)
425{
426	need_details = on_off;
427}
428
429
430void opreport_formatter::output(ostream & out, symbol_entry const * symb)
431{
432	do_output(out, *symb, symb->sample, counts);
433
434	if (need_details)
435		output_details(out, symb);
436}
437
438
439void opreport_formatter::
440output(ostream & out, symbol_collection const & syms)
441{
442	output_header(out);
443
444	symbol_collection::const_iterator it = syms.begin();
445	symbol_collection::const_iterator end = syms.end();
446	for (; it != end; ++it)
447		output(out, *it);
448}
449
450
451void opreport_formatter::
452output_details(ostream & out, symbol_entry const * symb)
453{
454	counts_t c = counts;
455
456	if (!global_percent)
457		c.total = symb->sample.counts;
458
459	// cumulated percent are relative to current symbol.
460	c.cumulated_samples = count_array_t();
461	c.cumulated_percent = count_array_t();
462
463	sample_container::samples_iterator it = profile.begin(symb);
464	sample_container::samples_iterator end = profile.end(symb);
465	for (; it != end; ++it) {
466		out << "  ";
467		do_output(out, *symb, it->second, c, diff_array_t(), true);
468	}
469}
470
471
472cg_formatter::cg_formatter(callgraph_container const & profile)
473{
474	counts.total = profile.samples_count();
475}
476
477
478void cg_formatter::output(ostream & out, cg_collection const & syms)
479{
480	// amount of spacing prefixing child and parent lines
481	string const child_parent_prefix("  ");
482
483	output_header(out);
484
485	out << string(79, '-') << endl;
486
487	cg_collection::const_iterator it;
488	cg_collection::const_iterator end = syms.end();
489
490	for (it = syms.begin(); it < end; ++it) {
491		cg_symbol const & sym = *it;
492
493		cg_symbol::children::const_iterator cit;
494		cg_symbol::children::const_iterator cend = sym.callers.end();
495
496		counts_t c;
497		if (global_percent)
498			c.total = counts.total;
499		else
500			c.total = sym.total_caller_count;
501
502		for (cit = sym.callers.begin(); cit != cend; ++cit) {
503			out << child_parent_prefix;
504			do_output(out, *cit, cit->sample, c);
505		}
506
507		do_output(out, sym, sym.sample, counts);
508
509		c = counts_t();
510		if (global_percent)
511			c.total = counts.total;
512		else
513			c.total = sym.total_callee_count;
514
515		cend = sym.callees.end();
516
517		for (cit = sym.callees.begin(); cit != cend; ++cit) {
518			out << child_parent_prefix;
519			do_output(out, *cit, cit->sample, c);
520		}
521
522		out << string(79, '-') << endl;
523	}
524}
525
526
527diff_formatter::diff_formatter(diff_container const & profile)
528{
529	counts.total = profile.samples_count();
530}
531
532
533void diff_formatter::output(ostream & out, diff_collection const & syms)
534{
535	output_header(out);
536
537	diff_collection::const_iterator it = syms.begin();
538	diff_collection::const_iterator end = syms.end();
539	for (; it != end; ++it)
540		do_output(out, *it, it->sample, counts, it->diffs);
541}
542
543
544} // namespace format_output
545