• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
36 using namespace std;
37 
38 namespace {
39 
40 static size_t nr_classes;
41 
42 /// storage for a merged file summary
43 struct summary {
44 	count_array_t counts;
45 	string lib_image;
46 
operator <__anon676736c50111::summary47 	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 
58 count_type summary::
add_files(list<profile_sample_files> const & files,size_t pclass)59 add_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  */
86 struct 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 
operator <__anon676736c50111::app_summary97 	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 
102 private:
103 	/// find a matching summary (including main app summary)
104 	summary & find_summary(string const & image);
105 };
106 
107 
find_summary(string const & image)108 summary & 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 
add_profile(profile_set const & profile,size_t pclass)124 count_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
151 struct 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 
161 summary_container::
summary_container(vector<profile_class> const & pclasses)162 summary_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 
output_header()202 void 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 
get_filename(string const & filename)218 string 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
output_count(count_type total_count,count_type count)225 void 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 
output_col_headers(bool indent)234 void 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 
284 void
output_deps(summary_container const & summaries,app_summary const & app)285 output_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  */
output_summaries(summary_container const & summaries)320 void 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 
get_format_flags(column_flags const & cf)342 format_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 
output_symbols(profile_container const & pc,bool multiple_apps)369 void 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 
output_diff_symbols(profile_container const & pc1,profile_container const & pc2,bool multiple_apps)415 void 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 
output_cg_symbols(callgraph_container const & cg,bool multiple_apps)448 void 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 
opreport(options::spec const & spec)496 int 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 
main(int argc,char const * argv[])587 int main(int argc, char const * argv[])
588 {
589 	cout.tie(0);
590 	return run_pp_tool(argc, argv, opreport);
591 }
592