Home | History | Annotate | Download | only in pp
      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 
     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 
     58 count_type summary::
     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 
     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 
    102 private:
    103 	/// find a matching summary (including main app summary)
    104 	summary & find_summary(string const & image);
    105 };
    106 
    107 
    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 
    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::
    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 
    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 
    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
    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 
    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
    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  */
    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 
    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 
    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 
    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 
    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 
    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 
    587 int main(int argc, char const * argv[])
    588 {
    589 	cout.tie(0);
    590 	return run_pp_tool(argc, argv, opreport);
    591 }
    592