1/**
2 * @file opreport_options.cpp
3 * Options for opreport tool
4 *
5 * @remark Copyright 2003 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author John Levon
9 * @author Philippe Elie
10 */
11
12#include <vector>
13#include <list>
14#include <iostream>
15#include <algorithm>
16#include <iterator>
17#include <fstream>
18
19#include "profile_spec.h"
20#include "arrange_profiles.h"
21#include "opreport_options.h"
22#include "popt_options.h"
23#include "string_filter.h"
24#include "file_manip.h"
25#include "xml_output.h"
26#include "xml_utils.h"
27#include "cverb.h"
28
29using namespace std;
30
31profile_classes classes;
32profile_classes classes2;
33
34namespace options {
35	demangle_type demangle = dmt_normal;
36	bool symbols;
37	bool callgraph;
38	bool debug_info;
39	bool details;
40	bool exclude_dependent;
41	string_filter symbol_filter;
42	sort_options sort_by;
43	merge_option merge_by;
44	bool show_header = true;
45	bool long_filenames;
46	bool show_address;
47	bool accumulated;
48	bool reverse_sort;
49	bool global_percent;
50	bool xml;
51	string xml_options;
52}
53
54
55namespace {
56
57string outfile;
58vector<string> mergespec;
59vector<string> sort;
60vector<string> exclude_symbols;
61vector<string> include_symbols;
62string demangle_option = "normal";
63
64popt::option options_array[] = {
65	popt::option(options::callgraph, "callgraph", 'c',
66	             "show call graph"),
67	popt::option(options::details, "details", 'd',
68		     "output detailed samples for each symbol"),
69	popt::option(options::symbols, "symbols", 'l',
70		     "list all symbols"),
71
72	popt::option(outfile, "output-file", 'o',
73	             "output to the given filename", "file"),
74
75	popt::option(sort, "sort", 's',
76		     "sort by", "sample,image,app-name,symbol,debug,vma"),
77	popt::option(options::reverse_sort, "reverse-sort", 'r',
78		     "use reverse sort"),
79	popt::option(mergespec, "merge", 'm',
80		     "comma separated list", "cpu,lib,tid,tgid,unitmask,all"),
81	popt::option(options::exclude_dependent, "exclude-dependent", 'x',
82		     "exclude libs, kernel, and module samples for applications"),
83	popt::option(exclude_symbols, "exclude-symbols", 'e',
84		     "exclude these comma separated symbols", "symbols"),
85	popt::option(include_symbols, "include-symbols", 'i',
86		     "include these comma separated symbols", "symbols"),
87	popt::option(options::threshold_opt, "threshold", 't',
88		     "minimum percentage needed to produce output",
89		     "percent"),
90
91	popt::option(demangle_option, "demangle", 'D',
92		     "demangle GNU C++ symbol names (default normal)",
93	             "none|normal|smart"),
94	// PP:5
95	popt::option(options::debug_info, "debug-info", 'g',
96		     "add source file and line number to output"),
97	popt::option(options::show_header, "no-header", 'n',
98		     "remove all headers from output"),
99	popt::option(options::show_address, "show-address", 'w',
100	             "show VMA address of each symbol"),
101	popt::option(options::long_filenames, "long-filenames", 'f',
102		     "show the full path of filenames"),
103	popt::option(options::accumulated, "accumulated", 'a',
104		     "percentage field show accumulated count"),
105	popt::option(options::global_percent, "global-percent", '%',
106		     "percentage are not relative to symbol count or image "
107		     "count but total sample count"),
108
109	popt::option(options::xml, "xml", 'X',
110		     "XML output"),
111
112};
113
114
115void handle_sort_option()
116{
117	if (options::xml && !sort.empty()) {
118		cerr << "warning: sort options ignored because they "
119		     << "are incompatible with --xml" << endl;
120		// don't allow any other sorting, except the default below,
121		// to mess up symbol traversal for XML
122		sort.clear();
123	}
124
125	if (sort.empty() || options::xml) {
126		// PP:5.14 sort default to sample
127		if (options::xml) {
128			// implicitly sort by app-name,image so that in the
129			// symbol traversal all library module symbols are
130			// grouped together with their application
131			sort.push_back("app-name");
132			sort.push_back("image");
133		} else
134			sort.push_back("sample");
135	}
136
137	vector<string>::const_iterator cit = sort.begin();
138	vector<string>::const_iterator end = sort.end();
139
140	for (; cit != end; ++cit)
141		options::sort_by.add_sort_option(*cit);
142}
143
144
145void handle_output_file()
146{
147	if (outfile.empty())
148		return;
149
150	static ofstream os(outfile.c_str());
151	if (!os) {
152		cerr << "Couldn't open \"" << outfile
153		     << "\" for writing." << endl;
154		exit(EXIT_FAILURE);
155	}
156
157	cout.rdbuf(os.rdbuf());
158}
159
160
161///  Check incompatible or meaningless options.
162void check_options(bool diff)
163{
164	using namespace options;
165
166	bool do_exit = false;
167
168	if (callgraph) {
169		symbols = true;
170		if (details) {
171			cerr << "--callgraph is incompatible with --details" << endl;
172			do_exit = true;
173		}
174
175		if (diff) {
176			cerr << "differential profiles are incompatible with --callgraph" << endl;
177			do_exit = true;
178		}
179	}
180
181	if (xml) {
182		if (accumulated) {
183			cerr << "--accumulated is incompatible with --xml" << endl;
184			do_exit = true;
185		}
186
187		if (global_percent) {
188			cerr << "--global_percent is incompatible with --xml" << endl;
189			do_exit = true;
190		}
191	}
192
193
194	if (details && diff) {
195		cerr << "differential profiles are incompatible with --details" << endl;
196		do_exit = true;
197	}
198
199	if (!symbols) {
200		if (diff) {
201			cerr << "different profiles are meaningless "
202				"without --symbols" << endl;
203			do_exit = true;
204		}
205
206		if (show_address) {
207			cerr << "--show-address is meaningless "
208				"without --symbols" << endl;
209			do_exit = true;
210		}
211
212		if (debug_info || accumulated) {
213			cerr << "--debug-info and --accumulated are "
214			     << "meaningless without --symbols" << endl;
215			do_exit = true;
216		}
217
218		if (!exclude_symbols.empty() || !include_symbols.empty()) {
219			cerr << "--exclude-symbols and --include-symbols are "
220			     << "meaningless without --symbols" << endl;
221			do_exit = true;
222		}
223
224		if (find(sort_by.options.begin(), sort_by.options.end(),
225			 sort_options::vma) != sort_by.options.end()) {
226			cerr << "--sort=vma is "
227			     << "meaningless without --symbols" << endl;
228			do_exit = true;
229		}
230	}
231
232	if (global_percent && symbols && !(details || callgraph)) {
233		cerr << "--global-percent is meaningless with --symbols "
234		        "and without --details or --callgraph" << endl;
235		do_exit = true;
236	}
237
238	if (do_exit)
239		exit(EXIT_FAILURE);
240}
241
242
243/// process a spec into classes
244void process_spec(profile_classes & classes, list<string> const & spec)
245{
246	using namespace options;
247
248	copy(spec.begin(), spec.end(),
249	     ostream_iterator<string>(cverb << vsfile, " "));
250	cverb << vsfile << "\n\n";
251
252	profile_spec const pspec =
253		profile_spec::create(spec, options::image_path,
254				     options::root_path);
255
256	list<string> sample_files = pspec.generate_file_list(exclude_dependent,
257	                                                     !options::callgraph);
258
259	cverb << vsfile << "Archive: " << pspec.get_archive_path() << endl;
260
261	cverb << vsfile << "Matched sample files: " << sample_files.size()
262	      << endl;
263	copy(sample_files.begin(), sample_files.end(),
264	     ostream_iterator<string>(cverb << vsfile, "\n"));
265
266	classes = arrange_profiles(sample_files, merge_by,
267				   pspec.extra_found_images);
268
269	cverb << vsfile << "profile_classes:\n" << classes << endl;
270
271	if (classes.v.empty()) {
272		cerr << "error: no sample files found: profile specification "
273		     "too strict ?" << endl;
274		exit(EXIT_FAILURE);
275	}
276}
277
278
279} // namespace anon
280
281
282void handle_options(options::spec const & spec)
283{
284	using namespace options;
285
286	if (details) {
287		symbols = true;
288		show_address = true;
289	}
290
291	if (options::xml) {
292		if (spec.common.size() != 0)
293			xml_utils::add_option(SESSION, spec.common);
294		if (debug_info)
295			xml_utils::add_option(DEBUG_INFO, true);
296		if (details)
297			xml_utils::add_option(DETAILS, true);
298		if (!image_path.empty())
299			xml_utils::add_option(IMAGE_PATH, image_path);
300		if (!mergespec.empty())
301			xml_utils::add_option(MERGE, mergespec);
302		if (exclude_dependent)
303			xml_utils::add_option(EXCLUDE_DEPENDENT, true);
304		if (!exclude_symbols.empty())
305			xml_utils::add_option(EXCLUDE_SYMBOLS, exclude_symbols);
306		if (!include_symbols.empty())
307			xml_utils::add_option(INCLUDE_SYMBOLS, include_symbols);
308	}
309
310	handle_sort_option();
311	merge_by = handle_merge_option(mergespec, true, exclude_dependent);
312	handle_output_file();
313	demangle = handle_demangle_option(demangle_option);
314	check_options(spec.first.size());
315
316	symbol_filter = string_filter(include_symbols, exclude_symbols);
317
318	if (!spec.first.size()) {
319		process_spec(classes, spec.common);
320	} else {
321		if (options::xml) {
322			cerr << "differential profiles are incompatible with --xml" << endl;
323			exit(EXIT_FAILURE);
324		}
325		cverb << vsfile << "profile spec 1:" << endl;
326		process_spec(classes, spec.first);
327		cverb << vsfile << "profile spec 2:" << endl;
328		process_spec(classes2, spec.second);
329
330		if (!classes.matches(classes2)) {
331			cerr << "profile classes are incompatible" << endl;
332			exit(EXIT_FAILURE);
333		}
334	}
335}
336