opreport.cpp revision 8cfa702f803c5ef6a2b062a489a1b2cf66b45b5e
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 "xml_utils.h"
34#include "image_errors.h"
35
36using namespace std;
37
38namespace {
39
40static size_t nr_classes;
41
42/// storage for a merged file summary
43struct summary {
44	count_array_t counts;
45	string lib_image;
46
47	bool operator<(summary const & rhs) const {
48		return options::reverse_sort
49		    ? counts[0] < rhs.counts[0] : rhs.counts[0] < counts[0];
50	}
51
52	/// add a set of files to a summary
53	count_type add_files(list<profile_sample_files> const & files,
54	                     size_t pclass);
55};
56
57
58count_type summary::
59add_files(list<profile_sample_files> const & files, size_t pclass)
60{
61	count_type subtotal = 0;
62
63	list<profile_sample_files>::const_iterator it = files.begin();
64	list<profile_sample_files>::const_iterator const end = files.end();
65
66	for (; it != end; ++it) {
67		count_type count = profile_t::sample_count(it->sample_filename);
68		counts[pclass] += count;
69		subtotal += count;
70
71		if (!it->cg_files.empty()) {
72			throw op_runtime_error("opreport.cpp::add_files(): "
73			       "unxpected non empty cg file set");
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	count_type 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
124count_type app_summary::add_profile(profile_set const & profile,
125                                size_t pclass)
126{
127	count_type group_total = 0;
128
129	// first the main image
130	summary & summ = find_summary(profile.image);
131	count_type 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		count_type 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	// sort by count
194	stable_sort(apps.begin(), apps.end());
195	vector<app_summary>::iterator ait = apps.begin();
196	vector<app_summary>::iterator const aend = apps.end();
197	for (; ait != aend; ++ait)
198		stable_sort(ait->deps.begin(), ait->deps.end());
199}
200
201
202void output_header()
203{
204	if (!options::show_header)
205		return;
206
207	cout << classes.cpuinfo << endl;
208	if (!classes.event.empty())
209		cout << classes.event << endl;
210
211	for (vector<profile_class>::size_type i = 0;
212	     i < classes.v.size(); ++i) {
213		cout << classes.v[i].longname << endl;
214	}
215}
216
217
218string get_filename(string const & filename)
219{
220	return options::long_filenames ? filename : op_basename(filename);
221}
222
223
224/// Output a count and a percentage
225void output_count(count_type total_count, count_type count)
226{
227	cout << setw(9) << count << ' ';
228	double ratio = op_ratio(count, total_count);
229	cout << format_percent(ratio * 100, percent_int_width,
230			      percent_fract_width) << ' ';
231}
232
233
234void output_col_headers(bool indent)
235{
236	if (!options::show_header)
237		return;
238
239	if (indent)
240		cout << '\t';
241
242	size_t colwidth = 9 + 1 + percent_width;
243
244	for (size_t i = 0; i < classes.v.size(); ++i) {
245		string name = classes.v[i].name;
246		if (name.length() > colwidth)
247			name = name.substr(0, colwidth - 3)
248				+ "...";
249		io_state state(cout);
250		// gcc 2.95 doesn't know right io manipulator
251		cout.setf(ios::right, ios::adjustfield);
252		// gcc 2.95 doesn't honor setw() for std::string
253		cout << setw(colwidth) << name.c_str();
254		cout << '|';
255	}
256	cout << '\n';
257
258	if (indent)
259		cout << '\t';
260
261	for (size_t i = 0; i < classes.v.size(); ++i) {
262		cout << "  samples| ";
263		io_state state(cout);
264		// gcc 2.95 doesn't know right io manipulator
265		cout.setf(ios::right, ios::adjustfield);
266		cout << setw(percent_width) << "%|";
267	}
268
269	cout << '\n';
270
271	if (indent)
272		cout << '\t';
273
274	for (size_t i = 0; i < classes.v.size(); ++i) {
275		cout << "-----------";
276		string str(percent_width, '-');
277		cout << str;
278	}
279
280	cout << '\n';
281}
282
283
284void
285output_deps(summary_container const & summaries,
286	    app_summary const & app)
287{
288	// the app summary itself is *always* present
289	// (perhaps with zero counts) so this test
290	// is correct
291	if (app.deps.size() == 1)
292		return;
293
294	output_col_headers(true);
295
296	for (size_t j = 0 ; j < app.deps.size(); ++j) {
297		summary const & summ = app.deps[j];
298
299		if (summ.counts.zero())
300			continue;
301
302		cout << '\t';
303
304		for (size_t i = 0; i < nr_classes; ++i) {
305			count_type tot_count = options::global_percent
306				? summaries.total_counts[i] : app.counts[i];
307
308			output_count(tot_count, summ.counts[i]);
309		}
310
311		cout << get_filename(summ.lib_image);
312		cout << '\n';
313	}
314}
315
316
317/**
318 * Display all the given summary information
319 */
320void output_summaries(summary_container const & summaries)
321{
322	output_col_headers(false);
323
324	for (size_t i = 0; i < summaries.apps.size(); ++i) {
325		app_summary const & app = summaries.apps[i];
326
327		if ((app.counts[0] * 100.0) / summaries.total_counts[0]
328		    < options::threshold) {
329			continue;
330		}
331
332		for (size_t j = 0; j < nr_classes; ++j)
333			output_count(summaries.total_counts[j], app.counts[j]);
334
335		cout << get_filename(app.image) << '\n';
336
337		output_deps(summaries, app);
338	}
339}
340
341
342format_flags get_format_flags(column_flags const & cf)
343{
344	format_flags flags(ff_none);
345	flags = format_flags(flags | ff_nr_samples);
346	flags = format_flags(flags | ff_percent | ff_symb_name);
347
348	if (options::show_address)
349		flags = format_flags(flags | ff_vma);
350
351	if (options::debug_info)
352		flags = format_flags(flags | ff_linenr_info);
353
354	if (options::accumulated) {
355		flags = format_flags(flags | ff_nr_samples_cumulated);
356		flags = format_flags(flags | ff_percent_cumulated);
357	}
358
359	if (classes2.v.size())
360		flags = format_flags(flags | ff_diff);
361
362	if (cf & cf_image_name)
363		flags = format_flags(flags | ff_image_name);
364
365	return flags;
366}
367
368
369void output_symbols(profile_container const & pc, bool multiple_apps)
370{
371	profile_container::symbol_choice choice;
372	choice.threshold = options::threshold;
373	symbol_collection symbols = pc.select_symbols(choice);
374	options::sort_by.sort(symbols, options::reverse_sort,
375	                      options::long_filenames);
376	format_output::formatter * out;
377	format_output::xml_formatter * xml_out = 0;
378	format_output::opreport_formatter * text_out = 0;
379
380	if (options::xml) {
381		xml_out = new format_output::xml_formatter(&pc, symbols,
382			pc.extra_found_images, options::symbol_filter);
383		xml_out->show_details(options::details);
384		out = xml_out;
385		// for XML always output long filenames
386		out->show_long_filenames(true);
387	} else {
388		text_out = new format_output::opreport_formatter(pc);
389		text_out->show_details(options::details);
390		out = text_out;
391		out->show_long_filenames(options::long_filenames);
392	}
393
394	out->set_nr_classes(nr_classes);
395	out->show_header(options::show_header);
396	out->vma_format_64bit(choice.hints & cf_64bit_vma);
397	out->show_global_percent(options::global_percent);
398
399	format_flags flags = get_format_flags(choice.hints);
400	if (multiple_apps)
401		flags = format_flags(flags | ff_app_name);
402
403	out->add_format(flags);
404
405	if (options::xml) {
406		xml_support = new xml_utils(xml_out, symbols, nr_classes,
407			pc.extra_found_images);
408		xml_out->output(cout);
409	} else {
410		text_out->output(cout, symbols);
411	}
412}
413
414
415void output_diff_symbols(profile_container const & pc1,
416                         profile_container const & pc2, bool multiple_apps)
417{
418	diff_container dc(pc1, pc2);
419
420	profile_container::symbol_choice choice;
421	choice.threshold = options::threshold;
422
423	diff_collection symbols = dc.get_symbols(choice);
424
425	format_flags flags = get_format_flags(choice.hints);
426	if (multiple_apps)
427		flags = format_flags(flags | ff_app_name);
428
429	// With diff profile we output only filename coming from the first
430	// profile session, internally we use only name derived from the sample
431	// filename so image name can match.
432	format_output::diff_formatter out(dc, pc1.extra_found_images);
433
434	out.set_nr_classes(nr_classes);
435	out.show_long_filenames(options::long_filenames);
436	out.show_header(options::show_header);
437	out.show_global_percent(options::global_percent);
438	out.vma_format_64bit(choice.hints & cf_64bit_vma);
439	out.add_format(flags);
440
441	options::sort_by.sort(symbols, options::reverse_sort,
442	                      options::long_filenames);
443
444	out.output(cout, symbols);
445}
446
447
448void output_cg_symbols(callgraph_container const & cg, bool multiple_apps)
449{
450	column_flags output_hints = cg.output_hint();
451
452	symbol_collection symbols = cg.get_symbols();
453
454	options::sort_by.sort(symbols, options::reverse_sort,
455	                      options::long_filenames);
456
457	format_output::formatter * out;
458	format_output::xml_cg_formatter * xml_out = 0;
459	format_output::cg_formatter * text_out = 0;
460
461	if (options::xml) {
462		xml_out = new format_output::xml_cg_formatter(cg, symbols,
463			options::symbol_filter);
464		xml_out->show_details(options::details);
465		out = xml_out;
466		// for XML always output long filenames
467		out->show_long_filenames(true);
468	} else {
469		text_out = new format_output::cg_formatter(cg);
470		out = text_out;
471		out->show_long_filenames(options::long_filenames);
472	}
473
474	out->set_nr_classes(nr_classes);
475	out->show_header(options::show_header);
476	out->vma_format_64bit(output_hints & cf_64bit_vma);
477	out->show_global_percent(options::global_percent);
478
479	format_flags flags = get_format_flags(output_hints);
480	if (multiple_apps)
481		flags = format_flags(flags | ff_app_name);
482
483	out->add_format(flags);
484
485	if (options::xml) {
486		xml_support = new xml_utils(xml_out, symbols, nr_classes,
487			cg.extra_found_images);
488		xml_out->output(cout);
489	} else {
490		text_out->output(cout, symbols);
491	}
492
493}
494
495
496int opreport(options::spec const & spec)
497{
498	want_xml = options::xml;
499
500	handle_options(spec);
501
502	nr_classes = classes.v.size();
503
504	if (!options::symbols && !options::xml) {
505		summary_container summaries(classes.v);
506		output_header();
507		output_summaries(summaries);
508		return 0;
509	}
510
511	bool multiple_apps = false;
512
513	for (size_t i = 0; i < classes.v.size(); ++i) {
514		if (classes.v[i].profiles.size() > 1)
515			multiple_apps = true;
516	}
517
518	list<inverted_profile> iprofiles = invert_profiles(classes);
519
520	report_image_errors(iprofiles, classes.extra_found_images);
521
522	if (options::xml) {
523		xml_utils::output_xml_header(options::command_options,
524		                             classes.cpuinfo, classes.event);
525	} else {
526		output_header();
527	}
528
529	if (classes2.v.size()) {
530		for (size_t i = 0; i < classes2.v.size(); ++i) {
531			if (classes2.v[i].profiles.size() > 1)
532				multiple_apps |= true;
533		}
534
535		profile_container pc1(options::debug_info, options::details,
536				      classes.extra_found_images);
537
538		list<inverted_profile>::iterator it = iprofiles.begin();
539		list<inverted_profile>::iterator const end = iprofiles.end();
540
541		for (; it != end; ++it)
542			populate_for_image(pc1, *it,
543					   options::symbol_filter, 0);
544
545		list<inverted_profile> iprofiles2 = invert_profiles(classes2);
546
547		report_image_errors(iprofiles2, classes2.extra_found_images);
548
549		profile_container pc2(options::debug_info, options::details,
550				      classes2.extra_found_images);
551
552		list<inverted_profile>::iterator it2 = iprofiles2.begin();
553		list<inverted_profile>::iterator const end2 = iprofiles2.end();
554
555		for (; it2 != end2; ++it2)
556			populate_for_image(pc2, *it2,
557					   options::symbol_filter, 0);
558
559		output_diff_symbols(pc1, pc2, multiple_apps);
560	} else if (options::callgraph) {
561		callgraph_container cg_container;
562		cg_container.populate(iprofiles, classes.extra_found_images,
563			options::debug_info, options::threshold,
564			options::merge_by.lib, options::symbol_filter);
565
566		output_cg_symbols(cg_container, multiple_apps);
567	} else {
568		profile_container samples(options::debug_info,
569			options::details, classes.extra_found_images);
570
571		list<inverted_profile>::iterator it = iprofiles.begin();
572		list<inverted_profile>::iterator const end = iprofiles.end();
573
574		for (; it != end; ++it)
575			populate_for_image(samples, *it,
576					   options::symbol_filter, 0);
577
578		output_symbols(samples, multiple_apps);
579	}
580
581	return 0;
582}
583
584}  // anonymous namespace
585
586
587int main(int argc, char const * argv[])
588{
589	cout.tie(0);
590	return run_pp_tool(argc, argv, opreport);
591}
592