opreport.cpp revision cc2ee177dbb3befca43e36cfc56778b006c3d050
1/**
2 * @file opreport.cpp
3 * Implement opreport utility
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 <iostream>
13#include <iomanip>
14#include <vector>
15#include <algorithm>
16#include <sstream>
17#include <numeric>
18
19#include "op_exception.h"
20#include "stream_util.h"
21#include "string_manip.h"
22#include "file_manip.h"
23#include "opreport_options.h"
24#include "op_header.h"
25#include "profile.h"
26#include "populate.h"
27#include "arrange_profiles.h"
28#include "profile_container.h"
29#include "callgraph_container.h"
30#include "diff_container.h"
31#include "symbol_sort.h"
32#include "format_output.h"
33#include "image_errors.h"
34
35using namespace std;
36
37namespace {
38
39static size_t nr_classes;
40
41/// storage for a merged file summary
42struct summary {
43	count_array_t counts;
44	string lib_image;
45
46	bool operator<(summary const & rhs) const {
47		return options::reverse_sort
48		    ? counts[0] < rhs.counts[0] : rhs.counts[0] < counts[0];
49	}
50
51	/// add a set of files to a summary
52	size_t add_files(list<profile_sample_files> const & files,
53			 size_t pclass);
54};
55
56
57size_t summary::
58add_files(list<profile_sample_files> const & files, size_t pclass)
59{
60	size_t subtotal = 0;
61
62	list<profile_sample_files>::const_iterator it = files.begin();
63	list<profile_sample_files>::const_iterator const end = files.end();
64
65	for (; it != end; ++it) {
66		size_t count = profile_t::sample_count(it->sample_filename);
67		counts[pclass] += count;
68		subtotal += count;
69
70		// FIXME: is it worth (for now yes I expect bugs ...)
71		if (!it->cg_files.empty()) {
72			throw "opreport.cpp::add_files(): unxpected non empty "
73				"cg file set\n";
74		}
75	}
76
77	return subtotal;
78}
79
80
81/**
82 * Summary of an application: a set of image summaries
83 * for one application, i.e. an application image and all
84 * dependent images such as libraries.
85 */
86struct app_summary {
87	/// total count of us and all dependents
88	count_array_t counts;
89	/// the main image
90	string image;
91	/// our dependent images
92	vector<summary> deps;
93
94	/// construct and fill in the data
95	size_t add_profile(profile_set const & profile, size_t pclass);
96
97	bool operator<(app_summary const & rhs) const {
98		return options::reverse_sort
99		    ? counts[0] < rhs.counts[0] : rhs.counts[0] < counts[0];
100	}
101
102private:
103	/// find a matching summary (including main app summary)
104	summary & find_summary(string const & image);
105};
106
107
108summary & app_summary::find_summary(string const & image)
109{
110	vector<summary>::iterator sit = deps.begin();
111	vector<summary>::iterator const send = deps.end();
112	for (; sit != send; ++sit) {
113		if (sit->lib_image == image)
114			return *sit;
115	}
116
117	summary summ;
118	summ.lib_image = image;
119	deps.push_back(summ);
120	return deps.back();
121}
122
123
124size_t app_summary::add_profile(profile_set const & profile,
125                                size_t pclass)
126{
127	size_t group_total = 0;
128
129	// first the main image
130	summary & summ = find_summary(profile.image);
131	size_t app_count = summ.add_files(profile.files, pclass);
132	counts[pclass] += app_count;
133	group_total += app_count;
134
135	// now all dependent images if any
136	list<profile_dep_set>::const_iterator it = profile.deps.begin();
137	list<profile_dep_set>::const_iterator const end = profile.deps.end();
138
139	for (; it != end; ++it) {
140		summary & summ = find_summary(it->lib_image);
141		size_t lib_count = summ.add_files(it->files, pclass);
142		counts[pclass] += lib_count;
143		group_total += lib_count;
144	}
145
146	return group_total;
147}
148
149
150/// all the summaries
151struct summary_container {
152	summary_container(vector<profile_class> const & pclasses);
153
154	/// all map summaries
155	vector<app_summary> apps;
156	/// total count of samples for all summaries
157	count_array_t total_counts;
158};
159
160
161summary_container::
162summary_container(vector<profile_class> const & pclasses)
163{
164	typedef map<string, app_summary> app_map_t;
165	app_map_t app_map;
166
167	for (size_t i = 0; i < pclasses.size(); ++i) {
168		list<profile_set>::const_iterator it
169			= pclasses[i].profiles.begin();
170		list<profile_set>::const_iterator const end
171			= pclasses[i].profiles.end();
172
173		for (; it != end; ++it) {
174			app_map_t::iterator ait = app_map.find(it->image);
175			if (ait == app_map.end()) {
176				app_summary app;
177				app.image = it->image;
178				total_counts[i] += app.add_profile(*it, i);
179				app_map[app.image] = app;
180			} else {
181				total_counts[i]
182					+= ait->second.add_profile(*it, i);
183			}
184		}
185	}
186
187	app_map_t::const_iterator it = app_map.begin();
188	app_map_t::const_iterator const end = app_map.end();
189
190	for (; it != end; ++it) {
191		apps.push_back(it->second);
192	}
193
194	// sort by count
195	stable_sort(apps.begin(), apps.end());
196	vector<app_summary>::iterator ait = apps.begin();
197	vector<app_summary>::iterator const aend = apps.end();
198	for (; ait != aend; ++ait) {
199		stable_sort(ait->deps.begin(), ait->deps.end());
200	}
201}
202
203
204void output_header()
205{
206	if (!options::show_header)
207		return;
208
209	cout << classes.cpuinfo << endl;
210	if (!classes.event.empty())
211		cout << classes.event << endl;
212
213	for (vector<profile_class>::size_type i = 0;
214	     i < classes.v.size(); ++i) {
215		cout << classes.v[i].longname << endl;
216	}
217}
218
219
220string get_filename(string const & filename)
221{
222	return options::long_filenames ? filename : op_basename(filename);
223}
224
225
226/// Output a count and a percentage
227void output_count(double total_count, size_t count)
228{
229	cout << setw(9) << count << ' ';
230	double ratio = op_ratio(count, total_count);
231	cout << format_percent(ratio * 100, percent_int_width,
232			      percent_fract_width) << ' ';
233}
234
235
236void output_col_headers(bool indent)
237{
238	if (!options::show_header)
239		return;
240
241	if (indent)
242		cout << '\t';
243
244	size_t colwidth = 9 + 1 + percent_width;
245
246	for (size_t i = 0; i < classes.v.size(); ++i) {
247		string name = classes.v[i].name;
248		if (name.length() > colwidth)
249			name = name.substr(0, colwidth - 3)
250				+ "...";
251		io_state state(cout);
252		// gcc 2.95 doesn't know right io manipulator
253		cout.setf(ios::right, ios::adjustfield);
254		// gcc 2.95 doesn't honor setw() for std::string
255		cout << setw(colwidth) << name.c_str();
256		cout << '|';
257	}
258	cout << '\n';
259
260	if (indent)
261		cout << '\t';
262
263	for (size_t i = 0; i < classes.v.size(); ++i) {
264		cout << "  samples| ";
265		io_state state(cout);
266		// gcc 2.95 doesn't know right io manipulator
267		cout.setf(ios::right, ios::adjustfield);
268		cout << setw(percent_width) << "%|";
269	}
270
271	cout << '\n';
272
273	if (indent)
274		cout << '\t';
275
276	for (size_t i = 0; i < classes.v.size(); ++i) {
277		cout << "-----------";
278		string str(percent_width, '-');
279		cout << str;
280	}
281
282	cout << '\n';
283}
284
285
286void
287output_deps(summary_container const & summaries,
288	    app_summary const & app)
289{
290	// the app summary itself is *always* present
291	// (perhaps with zero counts) so this test
292	// is correct
293	if (app.deps.size() == 1)
294		return;
295
296	output_col_headers(true);
297
298	for (size_t j = 0 ; j < app.deps.size(); ++j) {
299		summary const & summ = app.deps[j];
300
301		if (summ.counts.zero())
302			continue;
303
304		cout << '\t';
305
306		for (size_t i = 0; i < nr_classes; ++i) {
307			double tot_count = options::global_percent
308				? summaries.total_counts[i] : app.counts[i];
309
310			output_count(tot_count, summ.counts[i]);
311		}
312
313		cout << get_filename(summ.lib_image);
314		cout << '\n';
315	}
316}
317
318
319/**
320 * Display all the given summary information
321 */
322void output_summaries(summary_container const & summaries)
323{
324	output_col_headers(false);
325
326	for (size_t i = 0; i < summaries.apps.size(); ++i) {
327		app_summary const & app = summaries.apps[i];
328
329		if ((app.counts[0] * 100.0) / summaries.total_counts[0]
330		    < options::threshold) {
331			continue;
332		}
333
334		for (size_t j = 0; j < nr_classes; ++j) {
335			output_count(summaries.total_counts[j],
336			             app.counts[j]);
337		}
338
339		cout << get_filename(app.image) << '\n';
340
341		output_deps(summaries, app);
342	}
343}
344
345
346format_flags const get_format_flags(column_flags const & cf)
347{
348	format_flags flags(ff_none);
349	flags = format_flags(flags | ff_nr_samples);
350	flags = format_flags(flags | ff_percent | ff_symb_name);
351
352	if (options::show_address)
353		flags = format_flags(flags | ff_vma);
354
355	if (options::debug_info)
356		flags = format_flags(flags | ff_linenr_info);
357
358	if (options::accumulated) {
359		flags = format_flags(flags | ff_nr_samples_cumulated);
360		flags = format_flags(flags | ff_percent_cumulated);
361	}
362
363	if (classes2.v.size())
364		flags = format_flags(flags | ff_diff);
365
366	if (cf & cf_image_name)
367		flags = format_flags(flags | ff_image_name);
368
369	return flags;
370}
371
372
373void output_symbols(profile_container const & pc, bool multiple_apps)
374{
375	profile_container::symbol_choice choice;
376	choice.threshold = options::threshold;
377	symbol_collection symbols = pc.select_symbols(choice);
378	options::sort_by.sort(symbols, options::reverse_sort,
379	                      options::long_filenames);
380
381	format_output::opreport_formatter out(pc);
382
383	out.set_nr_classes(nr_classes);
384	out.show_details(options::details);
385	out.show_long_filenames(options::long_filenames);
386	out.show_header(options::show_header);
387	out.vma_format_64bit(choice.hints & cf_64bit_vma);
388	out.show_global_percent(options::global_percent);
389
390	format_flags flags = get_format_flags(choice.hints);
391	if (multiple_apps)
392		flags = format_flags(flags | ff_app_name);
393
394	out.add_format(flags);
395	out.output(cout, symbols);
396}
397
398
399void output_diff_symbols(profile_container const & pc1,
400                         profile_container const & pc2, bool multiple_apps)
401{
402	diff_container dc(pc1, pc2);
403
404	profile_container::symbol_choice choice;
405	choice.threshold = options::threshold;
406
407	diff_collection symbols = dc.get_symbols(choice);
408
409	format_flags flags = get_format_flags(choice.hints);
410	if (multiple_apps)
411		flags = format_flags(flags | ff_app_name);
412
413	format_output::diff_formatter out(dc);
414
415	out.set_nr_classes(nr_classes);
416	out.show_long_filenames(options::long_filenames);
417	out.show_header(options::show_header);
418	out.show_global_percent(options::global_percent);
419	out.vma_format_64bit(choice.hints & cf_64bit_vma);
420	out.add_format(flags);
421
422	options::sort_by.sort(symbols, options::reverse_sort,
423	                      options::long_filenames);
424
425	out.output(cout, symbols);
426}
427
428
429void output_cg_symbols(callgraph_container const & cg, bool multiple_apps)
430{
431	column_flags output_hints = cg.output_hint();
432
433	cg_collection symbols = cg.get_symbols();
434	options::sort_by.sort(symbols, options::reverse_sort,
435	                      options::long_filenames);
436
437	format_output::cg_formatter out(cg);
438
439	out.set_nr_classes(nr_classes);
440	out.show_long_filenames(options::long_filenames);
441	out.show_header(options::show_header);
442	out.vma_format_64bit(output_hints & cf_64bit_vma);
443	out.show_global_percent(options::global_percent);
444
445	format_flags flags = get_format_flags(output_hints);
446	if (multiple_apps)
447		flags = format_flags(flags | ff_app_name);
448
449	out.add_format(flags);
450
451	out.output(cout, symbols);
452}
453
454
455int opreport(options::spec const & spec)
456{
457	handle_options(spec);
458
459	nr_classes = classes.v.size();
460
461	if (!options::symbols) {
462		summary_container summaries(classes.v);
463		output_header();
464		output_summaries(summaries);
465		return 0;
466	}
467
468	bool multiple_apps = false;
469
470	for (size_t i = 0; i < classes.v.size(); ++i) {
471		if (classes.v[i].profiles.size() > 1)
472			multiple_apps = true;
473	}
474
475	list<inverted_profile> iprofiles
476		= invert_profiles(options::archive_path, classes,
477				  options::extra_found_images);
478
479	report_image_errors(iprofiles);
480
481	output_header();
482
483	if (classes2.v.size()) {
484		for (size_t i = 0; i < classes2.v.size(); ++i) {
485			if (classes2.v[i].profiles.size() > 1)
486				multiple_apps |= true;
487		}
488
489		profile_container pc1(options::debug_info, options::details);
490
491		list<inverted_profile>::iterator it = iprofiles.begin();
492		list<inverted_profile>::iterator const end = iprofiles.end();
493
494		for (; it != end; ++it)
495			populate_for_image(options::archive_path, pc1,
496				*it, options::symbol_filter, 0);
497
498		list<inverted_profile> iprofiles2
499			= invert_profiles(options::archive_path2, classes2,
500				  options::extra_found_images);
501
502		report_image_errors(iprofiles2);
503
504		profile_container pc2(options::debug_info, options::details);
505
506		list<inverted_profile>::iterator it2 = iprofiles2.begin();
507		list<inverted_profile>::iterator const end2 = iprofiles2.end();
508
509		for (; it2 != end2; ++it2)
510			populate_for_image(options::archive_path2, pc2,
511				*it2, options::symbol_filter, 0);
512
513		output_diff_symbols(pc1, pc2, multiple_apps);
514	} else if (options::callgraph) {
515		callgraph_container cg_container;
516		cg_container.populate(options::archive_path, iprofiles,
517			options::extra_found_images,
518			options::debug_info, options::threshold,
519			options::merge_by.lib, options::symbol_filter);
520
521		output_cg_symbols(cg_container, multiple_apps);
522	} else {
523		profile_container samples(options::debug_info, options::details);
524
525		list<inverted_profile>::iterator it = iprofiles.begin();
526		list<inverted_profile>::iterator const end = iprofiles.end();
527
528		for (; it != end; ++it)
529			populate_for_image(options::archive_path, samples,
530				*it, options::symbol_filter, 0);
531
532		output_symbols(samples, multiple_apps);
533	}
534
535	return 0;
536}
537
538}  // anonymous namespace
539
540
541int main(int argc, char const * argv[])
542{
543	cout.tie(0);
544	return run_pp_tool(argc, argv, opreport);
545}
546