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