1/**
2 * @file common_option.cpp
3 * Contains common options and implementation of entry point of pp tools
4 * and some miscelleaneous functions
5 *
6 * @remark Copyright 2003 OProfile authors
7 * @remark Read the file COPYING
8 *
9 * @author Philippe Elie
10 */
11
12#include <cstdlib>
13
14#include <iostream>
15#include <sstream>
16#include <iterator>
17#include <cstdlib>
18
19#include "op_config.h"
20#include "locate_images.h"
21#include "op_exception.h"
22#include "popt_options.h"
23#include "cverb.h"
24#include "common_option.h"
25#include "file_manip.h"
26
27using namespace std;
28
29namespace options {
30	double threshold = 0.0;
31	string threshold_opt;
32	string session_dir = OP_SESSION_DIR_DEFAULT;
33	string command_options;
34	vector<string> image_path;
35	string root_path;
36}
37
38namespace {
39
40vector<string> verbose_strings;
41
42popt::option common_options_array[] = {
43	popt::option(verbose_strings, "verbose", 'V',
44		     // FIXME help string for verbose level
45		     "verbose output", "all,debug,bfd,level1,sfile,stats,xml"),
46	popt::option(options::session_dir, "session-dir", '\0',
47		     "specify session path to hold samples database and session data (" OP_SESSION_DIR_DEFAULT ")", "path"),
48	popt::option(options::image_path, "image-path", 'p',
49		     "comma-separated path to search missing binaries", "path"),
50	popt::option(options::root_path, "root", 'R',
51		     "path to filesystem to search for missing binaries", "path"),
52};
53
54
55double handle_threshold(string threshold)
56{
57	double value = 0.0;
58
59	if (threshold.length()) {
60		istringstream ss(threshold);
61		if (!(ss >> value)) {
62			cerr << "illegal threshold value: " << threshold
63			     << " allowed range: [0-100]" << endl;
64			exit(EXIT_FAILURE);
65		}
66
67		if (value < 0.0 || value > 100.0) {
68			cerr << "illegal threshold value: " << threshold
69			     << " allowed range: [0-100]" << endl;
70			exit(EXIT_FAILURE);
71		}
72	}
73
74	cverb << vdebug << "threshold: " << value << endl;;
75
76	return value;
77}
78
79
80vector<string> pre_parse_spec(vector<string> const & non_options)
81{
82	vector<string> result;
83
84	for (size_t i = 0; i < non_options.size(); ++i) {
85		if (non_options[i] == "{}") {
86			result.push_back("{");
87			result.push_back("}");
88		} else {
89			result.push_back(non_options[i]);
90		}
91	}
92
93	return result;
94}
95
96
97options::spec const parse_spec(vector<string> non_options)
98{
99	bool in_first = false;
100	bool in_second = false;
101	bool first = false;
102	bool second = false;
103	options::spec pspec;
104
105	non_options = pre_parse_spec(non_options);
106
107	vector<string>::const_iterator it = non_options.begin();
108	vector<string>::const_iterator end = non_options.end();
109
110	for (; it != end; ++it) {
111		if (*it == "{") {
112			if (in_first || in_second || second)
113				goto fail;
114			if (first) {
115				in_second = true;
116				second = true;
117			} else {
118				in_first = true;
119				first = true;
120			}
121			continue;
122		}
123
124		if (*it == "}") {
125			if (in_first) {
126				in_first = false;
127			} else if (in_second) {
128				in_second = false;
129			} else {
130				goto fail;
131			}
132			continue;
133		}
134
135		if (in_first) {
136			pspec.first.push_back(*it);
137		} else if (in_second) {
138			pspec.second.push_back(*it);
139		} else {
140			pspec.common.push_back(*it);
141		}
142	}
143
144	if (in_first || in_second || (first && !second))
145		goto fail;
146
147	if (pspec.first.empty() && pspec.second.size())
148		goto fail;
149
150	if (first && second) {
151		pspec.first.insert(pspec.first.begin(), pspec.common.begin(),
152		                   pspec.common.end());
153		pspec.second.insert(pspec.second.begin(), pspec.common.begin(),
154		                   pspec.common.end());
155	}
156
157	return pspec;
158fail:
159	cerr << "invalid profile specification ";
160	copy(non_options.begin(), non_options.end(),
161	     ostream_iterator<string>(cerr, " "));
162	cerr << endl;
163	exit(EXIT_FAILURE);
164}
165
166
167options::spec get_options(int argc, char const * argv[])
168{
169	vector<string> non_options;
170	popt::parse_options(argc, argv, non_options);
171
172	// initialize paths in op_config.h
173	init_op_config_dirs(options::session_dir.c_str());
174
175	if (!options::threshold_opt.empty())
176		options::threshold = handle_threshold(options::threshold_opt);
177
178	if (!verbose::setup(verbose_strings)) {
179		cerr << "unknown --verbose= options\n";
180		exit(EXIT_FAILURE);
181	}
182
183	// XML generator needs command line options for its header
184	ostringstream str;
185	for (int i = 1; i < argc; ++i)
186		str << argv[i] << " ";
187	options::command_options = str.str();
188
189	return parse_spec(non_options);
190}
191
192}  // anon namespace
193
194
195int run_pp_tool(int argc, char const * argv[], pp_fct_run_t fct)
196{
197	try {
198		return fct(get_options(argc, argv));
199	}
200	catch (op_runtime_error const & e) {
201		cerr << argv[0] << " error: " << e.what() << endl;
202	}
203	catch (op_fatal_error const & e) {
204		cerr << argv[0] << " error: " << e.what() << endl;
205	}
206	catch (op_exception const & e) {
207		cerr << argv[0] << " error: " << e.what() << endl;
208	}
209	catch (invalid_argument const & e) {
210		cerr << argv[0] << " error: " << e.what() << endl;
211	}
212	catch (exception const & e) {
213		cerr << argv[0] << " error: " << e.what() << endl;
214	}
215	catch (...) {
216		cerr << argv[0] << " unknown exception" << endl;
217	}
218
219	return EXIT_FAILURE;
220}
221
222
223demangle_type handle_demangle_option(string const & option)
224{
225	if (option == "none")
226		return dmt_none;
227	if (option == "smart")
228		return dmt_smart;
229	if (option == "normal")
230		return dmt_normal;
231
232	throw op_runtime_error("invalid option --demangle=" + option);
233}
234
235
236merge_option handle_merge_option(vector<string> const & mergespec,
237    bool allow_lib, bool exclude_dependent)
238{
239	using namespace options;
240	merge_option merge_by;
241
242	merge_by.cpu = false;
243	merge_by.lib = false;
244	merge_by.tid = false;
245	merge_by.tgid = false;
246	merge_by.unitmask = false;
247
248	if (!allow_lib)
249		merge_by.lib = true;
250
251	bool is_all = false;
252
253	vector<string>::const_iterator cit = mergespec.begin();
254	vector<string>::const_iterator end = mergespec.end();
255
256	for (; cit != end; ++cit) {
257		if (*cit == "cpu") {
258			merge_by.cpu = true;
259		} else if (*cit == "tid") {
260			merge_by.tid = true;
261		} else if (*cit == "tgid") {
262			// PP:5.21 tgid merge imply tid merging.
263			merge_by.tgid = true;
264			merge_by.tid = true;
265		} else if ((*cit == "lib" || *cit == "library") && allow_lib) {
266			merge_by.lib = true;
267		} else if (*cit == "unitmask") {
268			merge_by.unitmask = true;
269		} else if (*cit == "all") {
270			merge_by.cpu = true;
271			merge_by.lib = true;
272			merge_by.tid = true;
273			merge_by.tgid = true;
274			merge_by.unitmask = true;
275			is_all = true;
276		} else {
277			cerr << "unknown merge option: " << *cit << endl;
278			exit(EXIT_FAILURE);
279		}
280	}
281
282	// if --merge all, don't warn about lib merging,
283	// it's not user friendly. Behaviour should still
284	// be correct.
285	if (exclude_dependent && merge_by.lib && allow_lib && !is_all) {
286		cerr << "--merge=lib is meaningless "
287		     << "with --exclude-dependent" << endl;
288		exit(EXIT_FAILURE);
289	}
290
291	return merge_by;
292}
293