blob: 700c0d6591d7b0af8948cf8fb7115a4bb2ee2e82 [file] [log] [blame]
/**
* @file format_output.cpp
* outputting format for symbol lists
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author Philippe Elie
* @author John Levon
*/
/* older glibc has C99 INFINITY in _GNU_SOURCE */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <sstream>
#include <iomanip>
#include <iostream>
#include <cmath>
#include "string_manip.h"
#include "format_output.h"
#include "profile_container.h"
#include "callgraph_container.h"
#include "diff_container.h"
using namespace std;
namespace {
string const & get_image_name(image_name_id id, bool lf)
{
return lf ? image_names.name(id) : image_names.basename(id);
}
string const get_linenr_info(file_location const floc, bool lf)
{
ostringstream out;
string const & filename = lf
? debug_names.name(floc.filename)
: debug_names.basename(floc.filename);
if (!filename.empty()) {
out << filename << ":" << floc.linenr;
} else {
out << "(no location information)";
}
return out.str();
}
string get_vma(bfd_vma vma, bool vma_64)
{
ostringstream out;
int width = vma_64 ? 16 : 8;
out << hex << setw(width) << setfill('0') << vma;
return out.str();
}
string get_percent(size_t dividend, size_t divisor)
{
double ratio = op_ratio(dividend, divisor);
return ::format_percent(ratio * 100, percent_int_width,
percent_fract_width);
}
} // anonymous namespace
namespace format_output {
formatter::formatter()
:
nr_classes(1),
flags(ff_none),
vma_64(false),
long_filenames(false),
need_header(true)
{
format_map[ff_vma] = field_description(9, "vma", &formatter::format_vma);
format_map[ff_nr_samples] = field_description(9, "samples", &formatter::format_nr_samples);
format_map[ff_nr_samples_cumulated] = field_description(14, "cum. samples", &formatter::format_nr_cumulated_samples);
format_map[ff_percent] = field_description(9, "%", &formatter::format_percent);
format_map[ff_percent_cumulated] = field_description(11, "cum. %", &formatter::format_cumulated_percent);
format_map[ff_linenr_info] = field_description(28, "linenr info", &formatter::format_linenr_info);
format_map[ff_image_name] = field_description(25, "image name", &formatter::format_image_name);
format_map[ff_app_name] = field_description(25, "app name", &formatter::format_app_name);
format_map[ff_symb_name] = field_description(30, "symbol name", &formatter::format_symb_name);
format_map[ff_percent_details] = field_description(9, "%", &formatter::format_percent_details);
format_map[ff_percent_cumulated_details] = field_description(10, "cum. %", &formatter::format_cumulated_percent_details);
format_map[ff_diff] = field_description(10, "diff %", &formatter::format_diff);
}
formatter::~formatter()
{
}
void formatter::set_nr_classes(size_t nr)
{
nr_classes = nr;
}
void formatter::add_format(format_flags flag)
{
flags = static_cast<format_flags>(flags | flag);
}
void formatter::show_header(bool on_off)
{
need_header = on_off;
}
void formatter::vma_format_64bit(bool on_off)
{
vma_64 = on_off;
}
void formatter::show_long_filenames(bool on_off)
{
long_filenames = on_off;
}
void formatter::show_global_percent(bool on_off)
{
global_percent = on_off;
}
void formatter::output_header(ostream & out)
{
if (!need_header)
return;
size_t padding = 0;
// first output the vma field
if (flags & ff_vma)
padding = output_header_field(out, ff_vma, padding);
// the field repeated for each profile class
for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
if (flags & ff_nr_samples)
padding = output_header_field(out,
ff_nr_samples, padding);
if (flags & ff_nr_samples_cumulated)
padding = output_header_field(out,
ff_nr_samples_cumulated, padding);
if (flags & ff_percent)
padding = output_header_field(out,
ff_percent, padding);
if (flags & ff_percent_cumulated)
padding = output_header_field(out,
ff_percent_cumulated, padding);
if (flags & ff_diff)
padding = output_header_field(out,
ff_diff, padding);
if (flags & ff_percent_details)
padding = output_header_field(out,
ff_percent_details, padding);
if (flags & ff_percent_cumulated_details)
padding = output_header_field(out,
ff_percent_cumulated_details, padding);
}
// now the remaining field
if (flags & ff_linenr_info)
padding = output_header_field(out, ff_linenr_info, padding);
if (flags & ff_image_name)
padding = output_header_field(out, ff_image_name, padding);
if (flags & ff_app_name)
padding = output_header_field(out, ff_app_name, padding);
if (flags & ff_symb_name)
padding = output_header_field(out, ff_symb_name, padding);
out << "\n";
}
/// describe each possible field of colummned output.
// FIXME: use % of the screen width here. sum of % equal to 100, then calculate
// ratio between 100 and the selected % to grow non fixed field use also
// lib[n?]curses to get the console width (look info source) (so on add a fixed
// field flags)
size_t formatter::
output_field(ostream & out, field_datum const & datum,
format_flags fl, size_t padding, bool hide_immutable)
{
if (!hide_immutable) {
out << string(padding, ' ');
field_description const & field(format_map[fl]);
string str = (this->*field.formatter)(datum);
out << str;
// at least one separator char
padding = 1;
if (str.length() < field.width)
padding = field.width - str.length();
} else {
field_description const & field(format_map[fl]);
padding += field.width;
}
return padding;
}
size_t formatter::
output_header_field(ostream & out, format_flags fl, size_t padding)
{
out << string(padding, ' ');
field_description const & field(format_map[fl]);
out << field.header_name;
// at least one separator char
padding = 1;
if (field.header_name.length() < field.width)
padding = field.width - field.header_name.length();
return padding;
}
string formatter::format_vma(field_datum const & f)
{
return get_vma(f.sample.vma, vma_64);
}
string formatter::format_symb_name(field_datum const & f)
{
return symbol_names.demangle(f.symbol.name);
}
string formatter::format_image_name(field_datum const & f)
{
return get_image_name(f.symbol.image_name, long_filenames);
}
string formatter::format_app_name(field_datum const & f)
{
return get_image_name(f.symbol.app_name, long_filenames);
}
string formatter::format_linenr_info(field_datum const & f)
{
return get_linenr_info(f.sample.file_loc, long_filenames);
}
string formatter::format_nr_samples(field_datum const & f)
{
ostringstream out;
out << f.sample.counts[f.pclass];
return out.str();
}
string formatter::format_nr_cumulated_samples(field_datum const & f)
{
if (f.diff == -INFINITY)
return "---";
ostringstream out;
f.counts.cumulated_samples[f.pclass] += f.sample.counts[f.pclass];
out << f.counts.cumulated_samples[f.pclass];
return out.str();
}
string formatter::format_percent(field_datum const & f)
{
if (f.diff == -INFINITY)
return "---";
return get_percent(f.sample.counts[f.pclass], f.counts.total[f.pclass]);
}
string formatter::format_cumulated_percent(field_datum const & f)
{
if (f.diff == -INFINITY)
return "---";
f.counts.cumulated_percent[f.pclass] += f.sample.counts[f.pclass];
return get_percent(f.counts.cumulated_percent[f.pclass],
f.counts.total[f.pclass]);
}
string formatter::format_percent_details(field_datum const & f)
{
return get_percent(f.sample.counts[f.pclass],
f.counts.total[f.pclass]);
}
string formatter::format_cumulated_percent_details(field_datum const & f)
{
f.counts.cumulated_percent_details[f.pclass] += f.sample.counts[f.pclass];
return get_percent(f.counts.cumulated_percent_details[f.pclass],
f.counts.total[f.pclass]);
}
string formatter::format_diff(field_datum const & f)
{
if (f.diff == INFINITY) {
ostringstream out;
out << "+++";
return out.str();
} else if (f.diff == -INFINITY) {
ostringstream out;
out << "---";
return out.str();
}
return ::format_percent(f.diff, percent_int_width,
percent_fract_width, true);
}
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)
{
size_t padding = 0;
// first output the vma field
field_datum datum(symb, sample, 0, c);
if (flags & ff_vma)
padding = output_field(out, datum, ff_vma, padding, false);
// repeated fields for each profile class
for (size_t pclass = 0 ; pclass < nr_classes; ++pclass) {
field_datum datum(symb, sample, pclass, c, diffs[pclass]);
if (flags & ff_nr_samples)
padding = output_field(out, datum,
ff_nr_samples, padding, false);
if (flags & ff_nr_samples_cumulated)
padding = output_field(out, datum,
ff_nr_samples_cumulated, padding, false);
if (flags & ff_percent)
padding = output_field(out, datum,
ff_percent, padding, false);
if (flags & ff_percent_cumulated)
padding = output_field(out, datum,
ff_percent_cumulated, padding, false);
if (flags & ff_diff)
padding = output_field(out, datum,
ff_diff, padding, false);
if (flags & ff_percent_details)
padding = output_field(out, datum,
ff_percent_details, padding, false);
if (flags & ff_percent_cumulated_details)
padding = output_field(out, datum,
ff_percent_cumulated_details, padding, false);
}
// now the remaining field
if (flags & ff_linenr_info)
padding = output_field(out, datum, ff_linenr_info,
padding, false);
if (flags & ff_image_name)
padding = output_field(out, datum, ff_image_name,
padding, hide_immutable);
if (flags & ff_app_name)
padding = output_field(out, datum, ff_app_name,
padding, hide_immutable);
if (flags & ff_symb_name)
padding = output_field(out, datum, ff_symb_name,
padding, hide_immutable);
out << "\n";
}
opreport_formatter::opreport_formatter(profile_container const & p)
:
profile(p),
need_details(false)
{
counts.total = profile.samples_count();
}
void opreport_formatter::show_details(bool on_off)
{
need_details = on_off;
}
void opreport_formatter::output(ostream & out, symbol_entry const * symb)
{
do_output(out, *symb, symb->sample, counts);
if (need_details)
output_details(out, symb);
}
void opreport_formatter::
output(ostream & out, symbol_collection const & syms)
{
output_header(out);
symbol_collection::const_iterator it = syms.begin();
symbol_collection::const_iterator end = syms.end();
for (; it != end; ++it)
output(out, *it);
}
void opreport_formatter::
output_details(ostream & out, symbol_entry const * symb)
{
counts_t c = counts;
if (!global_percent)
c.total = symb->sample.counts;
// cumulated percent are relative to current symbol.
c.cumulated_samples = count_array_t();
c.cumulated_percent = count_array_t();
sample_container::samples_iterator it = profile.begin(symb);
sample_container::samples_iterator end = profile.end(symb);
for (; it != end; ++it) {
out << " ";
do_output(out, *symb, it->second, c, diff_array_t(), true);
}
}
cg_formatter::cg_formatter(callgraph_container const & profile)
{
counts.total = profile.samples_count();
}
void cg_formatter::output(ostream & out, cg_collection const & syms)
{
// amount of spacing prefixing child and parent lines
string const child_parent_prefix(" ");
output_header(out);
out << string(79, '-') << endl;
cg_collection::const_iterator it;
cg_collection::const_iterator end = syms.end();
for (it = syms.begin(); it < end; ++it) {
cg_symbol const & sym = *it;
cg_symbol::children::const_iterator cit;
cg_symbol::children::const_iterator cend = sym.callers.end();
counts_t c;
if (global_percent)
c.total = counts.total;
else
c.total = sym.total_caller_count;
for (cit = sym.callers.begin(); cit != cend; ++cit) {
out << child_parent_prefix;
do_output(out, *cit, cit->sample, c);
}
do_output(out, sym, sym.sample, counts);
c = counts_t();
if (global_percent)
c.total = counts.total;
else
c.total = sym.total_callee_count;
cend = sym.callees.end();
for (cit = sym.callees.begin(); cit != cend; ++cit) {
out << child_parent_prefix;
do_output(out, *cit, cit->sample, c);
}
out << string(79, '-') << endl;
}
}
diff_formatter::diff_formatter(diff_container const & profile)
{
counts.total = profile.samples_count();
}
void diff_formatter::output(ostream & out, diff_collection const & syms)
{
output_header(out);
diff_collection::const_iterator it = syms.begin();
diff_collection::const_iterator end = syms.end();
for (; it != end; ++it)
do_output(out, *it, it->sample, counts, it->diffs);
}
} // namespace format_output