/**
* @file opreport_options.cpp
* Options for opreport tool
*
* @remark Copyright 2003 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon
* @author Philippe Elie
*/
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <fstream>
#include "profile_spec.h"
#include "arrange_profiles.h"
#include "opreport_options.h"
#include "popt_options.h"
#include "string_filter.h"
#include "file_manip.h"
#include "xml_output.h"
#include "xml_utils.h"
#include "cverb.h"
using namespace std;
profile_classes classes;
profile_classes classes2;
namespace options {
demangle_type demangle = dmt_normal;
bool symbols;
bool callgraph;
bool debug_info;
bool details;
bool exclude_dependent;
string_filter symbol_filter;
sort_options sort_by;
merge_option merge_by;
bool show_header = true;
bool long_filenames;
bool show_address;
bool accumulated;
bool reverse_sort;
bool global_percent;
bool xml;
string xml_options;
}
namespace {
string outfile;
vector<string> mergespec;
vector<string> sort;
vector<string> exclude_symbols;
vector<string> include_symbols;
string demangle_option = "normal";
popt::option options_array[] = {
popt::option(options::callgraph, "callgraph", 'c',
"show call graph"),
popt::option(options::details, "details", 'd',
"output detailed samples for each symbol"),
popt::option(options::symbols, "symbols", 'l',
"list all symbols"),
popt::option(outfile, "output-file", 'o',
"output to the given filename", "file"),
popt::option(sort, "sort", 's',
"sort by", "sample,image,app-name,symbol,debug,vma"),
popt::option(options::reverse_sort, "reverse-sort", 'r',
"use reverse sort"),
popt::option(mergespec, "merge", 'm',
"comma separated list", "cpu,lib,tid,tgid,unitmask,all"),
popt::option(options::exclude_dependent, "exclude-dependent", 'x',
"exclude libs, kernel, and module samples for applications"),
popt::option(exclude_symbols, "exclude-symbols", 'e',
"exclude these comma separated symbols", "symbols"),
popt::option(include_symbols, "include-symbols", 'i',
"include these comma separated symbols", "symbols"),
popt::option(options::threshold_opt, "threshold", 't',
"minimum percentage needed to produce output",
"percent"),
popt::option(demangle_option, "demangle", 'D',
"demangle GNU C++ symbol names (default normal)",
"none|normal|smart"),
// PP:5
popt::option(options::debug_info, "debug-info", 'g',
"add source file and line number to output"),
popt::option(options::show_header, "no-header", 'n',
"remove all headers from output"),
popt::option(options::show_address, "show-address", 'w',
"show VMA address of each symbol"),
popt::option(options::long_filenames, "long-filenames", 'f',
"show the full path of filenames"),
popt::option(options::accumulated, "accumulated", 'a',
"percentage field show accumulated count"),
popt::option(options::global_percent, "global-percent", '%',
"percentage are not relative to symbol count or image "
"count but total sample count"),
popt::option(options::xml, "xml", 'X',
"XML output"),
};
void handle_sort_option()
{
if (options::xml && !sort.empty()) {
cerr << "warning: sort options ignored because they "
<< "are incompatible with --xml" << endl;
// don't allow any other sorting, except the default below,
// to mess up symbol traversal for XML
sort.clear();
}
if (sort.empty() || options::xml) {
// PP:5.14 sort default to sample
if (options::xml) {
// implicitly sort by app-name,image so that in the
// symbol traversal all library module symbols are
// grouped together with their application
sort.push_back("app-name");
sort.push_back("image");
} else
sort.push_back("sample");
}
vector<string>::const_iterator cit = sort.begin();
vector<string>::const_iterator end = sort.end();
for (; cit != end; ++cit)
options::sort_by.add_sort_option(*cit);
}
void handle_output_file()
{
if (outfile.empty())
return;
static ofstream os(outfile.c_str());
if (!os) {
cerr << "Couldn't open \"" << outfile
<< "\" for writing." << endl;
exit(EXIT_FAILURE);
}
cout.rdbuf(os.rdbuf());
}
/// Check incompatible or meaningless options.
void check_options(bool diff)
{
using namespace options;
bool do_exit = false;
if (callgraph) {
symbols = true;
if (details) {
cerr << "--callgraph is incompatible with --details" << endl;
do_exit = true;
}
if (diff) {
cerr << "differential profiles are incompatible with --callgraph" << endl;
do_exit = true;
}
}
if (xml) {
if (accumulated) {
cerr << "--accumulated is incompatible with --xml" << endl;
do_exit = true;
}
if (global_percent) {
cerr << "--global_percent is incompatible with --xml" << endl;
do_exit = true;
}
}
if (details && diff) {
cerr << "differential profiles are incompatible with --details" << endl;
do_exit = true;
}
if (!symbols) {
if (diff) {
cerr << "different profiles are meaningless "
"without --symbols" << endl;
do_exit = true;
}
if (show_address) {
cerr << "--show-address is meaningless "
"without --symbols" << endl;
do_exit = true;
}
if (debug_info || accumulated) {
cerr << "--debug-info and --accumulated are "
<< "meaningless without --symbols" << endl;
do_exit = true;
}
if (!exclude_symbols.empty() || !include_symbols.empty()) {
cerr << "--exclude-symbols and --include-symbols are "
<< "meaningless without --symbols" << endl;
do_exit = true;
}
if (find(sort_by.options.begin(), sort_by.options.end(),
sort_options::vma) != sort_by.options.end()) {
cerr << "--sort=vma is "
<< "meaningless without --symbols" << endl;
do_exit = true;
}
}
if (global_percent && symbols && !(details || callgraph)) {
cerr << "--global-percent is meaningless with --symbols "
"and without --details or --callgraph" << endl;
do_exit = true;
}
if (do_exit)
exit(EXIT_FAILURE);
}
/// process a spec into classes
void process_spec(profile_classes & classes, list<string> const & spec)
{
using namespace options;
copy(spec.begin(), spec.end(),
ostream_iterator<string>(cverb << vsfile, " "));
cverb << vsfile << "\n\n";
profile_spec const pspec =
profile_spec::create(spec, options::image_path,
options::root_path);
list<string> sample_files = pspec.generate_file_list(exclude_dependent,
!options::callgraph);
cverb << vsfile << "Archive: " << pspec.get_archive_path() << endl;
cverb << vsfile << "Matched sample files: " << sample_files.size()
<< endl;
copy(sample_files.begin(), sample_files.end(),
ostream_iterator<string>(cverb << vsfile, "\n"));
classes = arrange_profiles(sample_files, merge_by,
pspec.extra_found_images);
cverb << vsfile << "profile_classes:\n" << classes << endl;
if (classes.v.empty()) {
cerr << "error: no sample files found: profile specification "
"too strict ?" << endl;
exit(EXIT_FAILURE);
}
}
} // namespace anon
void handle_options(options::spec const & spec)
{
using namespace options;
if (details) {
symbols = true;
show_address = true;
}
if (options::xml) {
if (spec.common.size() != 0)
xml_utils::add_option(SESSION, spec.common);
if (debug_info)
xml_utils::add_option(DEBUG_INFO, true);
if (details)
xml_utils::add_option(DETAILS, true);
if (!image_path.empty())
xml_utils::add_option(IMAGE_PATH, image_path);
if (!mergespec.empty())
xml_utils::add_option(MERGE, mergespec);
if (exclude_dependent)
xml_utils::add_option(EXCLUDE_DEPENDENT, true);
if (!exclude_symbols.empty())
xml_utils::add_option(EXCLUDE_SYMBOLS, exclude_symbols);
if (!include_symbols.empty())
xml_utils::add_option(INCLUDE_SYMBOLS, include_symbols);
}
handle_sort_option();
merge_by = handle_merge_option(mergespec, true, exclude_dependent);
handle_output_file();
demangle = handle_demangle_option(demangle_option);
check_options(spec.first.size());
symbol_filter = string_filter(include_symbols, exclude_symbols);
if (!spec.first.size()) {
process_spec(classes, spec.common);
} else {
if (options::xml) {
cerr << "differential profiles are incompatible with --xml" << endl;
exit(EXIT_FAILURE);
}
cverb << vsfile << "profile spec 1:" << endl;
process_spec(classes, spec.first);
cverb << vsfile << "profile spec 2:" << endl;
process_spec(classes2, spec.second);
if (!classes.matches(classes2)) {
cerr << "profile classes are incompatible" << endl;
exit(EXIT_FAILURE);
}
}
}