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