/**
* @file common_option.cpp
* Contains common options and implementation of entry point of pp tools
* and some miscelleaneous functions
*
* @remark Copyright 2003 OProfile authors
* @remark Read the file COPYING
*
* @author Philippe Elie
*/
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <iterator>
#include <cstdlib>
#include "op_config.h"
#include "locate_images.h"
#include "op_exception.h"
#include "popt_options.h"
#include "cverb.h"
#include "common_option.h"
#include "file_manip.h"
using namespace std;
namespace options {
double threshold = 0.0;
string threshold_opt;
string session_dir = OP_SESSION_DIR_DEFAULT;
string command_options;
vector<string> image_path;
string root_path;
}
namespace {
vector<string> verbose_strings;
popt::option common_options_array[] = {
popt::option(verbose_strings, "verbose", 'V',
// FIXME help string for verbose level
"verbose output", "all,debug,bfd,level1,sfile,stats,xml"),
popt::option(options::session_dir, "session-dir", '\0',
"specify session path to hold samples database and session data (" OP_SESSION_DIR_DEFAULT ")", "path"),
popt::option(options::image_path, "image-path", 'p',
"comma-separated path to search missing binaries", "path"),
popt::option(options::root_path, "root", 'R',
"path to filesystem to search for missing binaries", "path"),
};
double handle_threshold(string threshold)
{
double value = 0.0;
if (threshold.length()) {
istringstream ss(threshold);
if (!(ss >> value)) {
cerr << "illegal threshold value: " << threshold
<< " allowed range: [0-100]" << endl;
exit(EXIT_FAILURE);
}
if (value < 0.0 || value > 100.0) {
cerr << "illegal threshold value: " << threshold
<< " allowed range: [0-100]" << endl;
exit(EXIT_FAILURE);
}
}
cverb << vdebug << "threshold: " << value << endl;;
return value;
}
vector<string> pre_parse_spec(vector<string> const & non_options)
{
vector<string> result;
for (size_t i = 0; i < non_options.size(); ++i) {
if (non_options[i] == "{}") {
result.push_back("{");
result.push_back("}");
} else {
result.push_back(non_options[i]);
}
}
return result;
}
options::spec const parse_spec(vector<string> non_options)
{
bool in_first = false;
bool in_second = false;
bool first = false;
bool second = false;
options::spec pspec;
non_options = pre_parse_spec(non_options);
vector<string>::const_iterator it = non_options.begin();
vector<string>::const_iterator end = non_options.end();
for (; it != end; ++it) {
if (*it == "{") {
if (in_first || in_second || second)
goto fail;
if (first) {
in_second = true;
second = true;
} else {
in_first = true;
first = true;
}
continue;
}
if (*it == "}") {
if (in_first) {
in_first = false;
} else if (in_second) {
in_second = false;
} else {
goto fail;
}
continue;
}
if (in_first) {
pspec.first.push_back(*it);
} else if (in_second) {
pspec.second.push_back(*it);
} else {
pspec.common.push_back(*it);
}
}
if (in_first || in_second || (first && !second))
goto fail;
if (pspec.first.empty() && pspec.second.size())
goto fail;
if (first && second) {
pspec.first.insert(pspec.first.begin(), pspec.common.begin(),
pspec.common.end());
pspec.second.insert(pspec.second.begin(), pspec.common.begin(),
pspec.common.end());
}
return pspec;
fail:
cerr << "invalid profile specification ";
copy(non_options.begin(), non_options.end(),
ostream_iterator<string>(cerr, " "));
cerr << endl;
exit(EXIT_FAILURE);
}
options::spec get_options(int argc, char const * argv[])
{
vector<string> non_options;
popt::parse_options(argc, argv, non_options);
// initialize paths in op_config.h
init_op_config_dirs(options::session_dir.c_str());
if (!options::threshold_opt.empty())
options::threshold = handle_threshold(options::threshold_opt);
if (!verbose::setup(verbose_strings)) {
cerr << "unknown --verbose= options\n";
exit(EXIT_FAILURE);
}
// XML generator needs command line options for its header
ostringstream str;
for (int i = 1; i < argc; ++i)
str << argv[i] << " ";
options::command_options = str.str();
return parse_spec(non_options);
}
} // anon namespace
int run_pp_tool(int argc, char const * argv[], pp_fct_run_t fct)
{
try {
return fct(get_options(argc, argv));
}
catch (op_runtime_error const & e) {
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (op_fatal_error const & e) {
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (op_exception const & e) {
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (invalid_argument const & e) {
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (exception const & e) {
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (...) {
cerr << argv[0] << " unknown exception" << endl;
}
return EXIT_FAILURE;
}
demangle_type handle_demangle_option(string const & option)
{
if (option == "none")
return dmt_none;
if (option == "smart")
return dmt_smart;
if (option == "normal")
return dmt_normal;
throw op_runtime_error("invalid option --demangle=" + option);
}
merge_option handle_merge_option(vector<string> const & mergespec,
bool allow_lib, bool exclude_dependent)
{
using namespace options;
merge_option merge_by;
merge_by.cpu = false;
merge_by.lib = false;
merge_by.tid = false;
merge_by.tgid = false;
merge_by.unitmask = false;
if (!allow_lib)
merge_by.lib = true;
bool is_all = false;
vector<string>::const_iterator cit = mergespec.begin();
vector<string>::const_iterator end = mergespec.end();
for (; cit != end; ++cit) {
if (*cit == "cpu") {
merge_by.cpu = true;
} else if (*cit == "tid") {
merge_by.tid = true;
} else if (*cit == "tgid") {
// PP:5.21 tgid merge imply tid merging.
merge_by.tgid = true;
merge_by.tid = true;
} else if ((*cit == "lib" || *cit == "library") && allow_lib) {
merge_by.lib = true;
} else if (*cit == "unitmask") {
merge_by.unitmask = true;
} else if (*cit == "all") {
merge_by.cpu = true;
merge_by.lib = true;
merge_by.tid = true;
merge_by.tgid = true;
merge_by.unitmask = true;
is_all = true;
} else {
cerr << "unknown merge option: " << *cit << endl;
exit(EXIT_FAILURE);
}
}
// if --merge all, don't warn about lib merging,
// it's not user friendly. Behaviour should still
// be correct.
if (exclude_dependent && merge_by.lib && allow_lib && !is_all) {
cerr << "--merge=lib is meaningless "
<< "with --exclude-dependent" << endl;
exit(EXIT_FAILURE);
}
return merge_by;
}