00001
00012 #include <iostream>
00013 #include <sstream>
00014 #include <algorithm>
00015 #include <iomanip>
00016 #include <fstream>
00017 #include <utility>
00018
00019 #include "op_exception.h"
00020 #include "op_header.h"
00021 #include "profile.h"
00022 #include "populate.h"
00023 #include "op_sample_file.h"
00024 #include "cverb.h"
00025 #include "string_manip.h"
00026 #include "demangle_symbol.h"
00027 #include "child_reader.h"
00028 #include "op_file.h"
00029 #include "file_manip.h"
00030 #include "arrange_profiles.h"
00031 #include "opannotate_options.h"
00032 #include "profile_container.h"
00033 #include "symbol_sort.h"
00034 #include "image_errors.h"
00035
00036 using namespace std;
00037 using namespace options;
00038
00039 namespace {
00040
00041 size_t nr_events;
00042
00043 scoped_ptr<profile_container> samples;
00044
00046 string cmdline;
00047
00049 string annotation_fill;
00050
00052 string const begin_comment("/* ");
00053 string const in_comment(" * ");
00054 string const end_comment(" */");
00055
00057 unsigned int const count_width = 6;
00058
00059 string get_annotation_fill()
00060 {
00061 string str;
00062
00063 for (size_t i = 0; i < nr_events; ++i) {
00064 str += string(count_width, ' ') + ' ';
00065 str += string(percent_width, ' ');
00066 }
00067
00068 for (size_t i = 1; i < nr_events; ++i)
00069 str += " ";
00070
00071 str += " :";
00072 return str;
00073 }
00074
00075
00076 symbol_entry const * find_symbol(string const & image_name,
00077 string const & str_vma, bfd_vma vma_adj)
00078 {
00079
00080
00081
00082
00083
00084 bfd_vma vma = strtoull(str_vma.c_str(), NULL, 16) - vma_adj;
00085
00086 return samples->find_symbol(image_name, vma);
00087 }
00088
00089
00090 void output_info(ostream & out)
00091 {
00092 out << begin_comment << '\n';
00093
00094 out << in_comment << "Command line: " << cmdline << '\n'
00095 << in_comment << '\n';
00096
00097 out << in_comment << "Interpretation of command line:" << '\n';
00098
00099 if (!assembly) {
00100 out << in_comment
00101 << "Output annotated source file with samples" << '\n';
00102
00103 if (options::threshold != 0) {
00104 out << in_comment
00105 << "Output files where samples count reach "
00106 << options::threshold << "% of the samples\n";
00107 } else {
00108 out << in_comment << "Output all files" << '\n';
00109 }
00110 } else {
00111 out << in_comment
00112 << "Output annotated assembly listing with samples"
00113 << '\n';
00114
00115 if (!objdump_params.empty()) {
00116 out << in_comment << "Passing the following "
00117 "additional arguments to objdump ; \"";
00118 for (size_t i = 0 ; i < objdump_params.size() ; ++i)
00119 out << objdump_params[i] << " ";
00120 out << "\"" << '\n';
00121 }
00122 }
00123
00124 out << in_comment << '\n';
00125
00126 out << in_comment << classes.cpuinfo << endl;
00127 if (!classes.event.empty())
00128 out << in_comment << classes.event << endl;
00129
00130 for (size_t i = 0; i < classes.v.size(); ++i)
00131 out << in_comment << classes.v[i].longname << endl;
00132
00133 out << end_comment << '\n';
00134 }
00135
00136
00137 string count_str(count_array_t const & count,
00138 count_array_t const & total)
00139 {
00140 ostringstream os;
00141 for (size_t i = 0; i < nr_events; ++i) {
00142 os << setw(count_width) << count[i] << ' ';
00143
00144 os << format_percent(op_ratio(count[i], total[i]) * 100.0,
00145 percent_int_width, percent_fract_width);
00146 }
00147 return os.str();
00148 }
00149
00150
00163 int asm_list_annotation(symbol_entry const * last_symbol,
00164 bfd_vma last_symbol_vma,
00165 list<string>::iterator sit,
00166 sample_container::samples_iterator & samp_it,
00167 list<string> & asm_lines, bfd_vma vma_adj)
00168 {
00169 int ret = 0;
00170
00171 sample_entry const * sample = NULL;
00172
00173 if (samp_it != samples->end())
00174 sample = &samp_it->second;
00175
00176
00177
00178
00179
00180
00181
00182 bfd_vma vma = strtoull((*sit).c_str(), NULL, 16) - vma_adj;
00183
00184 if (sample
00185 && ((sample->vma < last_symbol_vma) || (sample->vma > vma))) {
00186 *sit = annotation_fill + *sit;
00187 } else if (sample && sample->vma == vma) {
00188
00189 string str = count_str(sample->counts, samples->samples_count());
00190
00191
00192 for (size_t i = 1; i < nr_events; ++i)
00193 str += " ";
00194
00195 *sit = str + " :" + *sit;
00196 if (samp_it != samples->end())
00197 ++samp_it;
00198
00199 } else if (sample && sample->vma < vma) {
00200
00201
00202
00203 list<string>::iterator sit_prev = sit;
00204 string prev_line, prev_vma_str;
00205 string::size_type loc1 = string::npos, loc2 = string::npos;
00206 while (sit_prev != asm_lines.begin()) {
00207 --sit_prev;
00208 prev_line = *sit_prev;
00209
00210 loc1 = prev_line.find(":", 0);
00211 if (loc1 != string::npos) {
00212 loc2 = prev_line.find(":", loc1+1);
00213 if (loc2 != string::npos) {
00214 prev_vma_str = prev_line.substr(loc1+1, loc2);
00215 break;
00216 }
00217 }
00218 }
00219
00220 bfd_vma prev_vma = strtoull(prev_vma_str.c_str(), NULL, 16);
00221
00222
00223 if (prev_vma != 0 && prev_vma < sample->vma) {
00224 string str;
00225
00226
00227 sample_entry * prev_sample = (sample_entry *)samples->
00228 find_sample(last_symbol, prev_vma);
00229 if (prev_sample) {
00230
00231 prev_sample->counts += sample->counts;
00232 str = count_str(prev_sample->counts, samples->samples_count());
00233 } else {
00234 str = count_str(sample->counts, samples->samples_count());
00235 }
00236
00237
00238 for (size_t i = 1; i < nr_events; ++i)
00239 str += " ";
00240
00241 *sit_prev = str + " :" + prev_line.substr(loc1+1);
00242 if (samp_it != samples->end())
00243 ++samp_it;
00244 ret = -1;
00245 } else {
00246
00247 *sit = annotation_fill + *sit;
00248 if (samp_it != samples->end())
00249 ++samp_it;
00250 }
00251 } else {
00252
00253 *sit = annotation_fill + *sit;
00254 }
00255
00256 return ret;
00257 }
00258
00259
00260 string symbol_annotation(symbol_entry const * symbol)
00261 {
00262 if (!symbol)
00263 return string();
00264
00265 string annot = count_str(symbol->sample.counts,
00266 samples->samples_count());
00267
00268 string const & symname = symbol_names.demangle(symbol->name);
00269
00270 string str = " ";
00271 str += begin_comment + symname + " total: ";
00272 str += count_str(symbol->sample.counts, samples->samples_count());
00273 str += end_comment;
00274 return str;
00275 }
00276
00277
00281 bool is_symbol_line(string const & str, string::size_type pos)
00282 {
00283 if (str[pos] != ' ' || str[pos + 1] != '<')
00284 return false;
00285
00286 return str[str.length() - 1] == ':';
00287 }
00288
00289
00290 void annotate_objdump_str_list(string const & app_name,
00291 symbol_collection const & symbols,
00292 list<string> & asm_lines)
00293 {
00294 symbol_entry const * last_symbol = 0;
00295 bfd_vma last_symbol_vma = 0;
00296 bfd_vma vma_adj;
00297 int ret = 0;
00298
00299
00300 bool do_output = true;
00301
00302 vma_adj = symbols[0]->vma_adj;
00303
00304
00305
00306 list<string>::iterator sit = asm_lines.begin();
00307 list<string>::iterator send = asm_lines.end();
00308 sample_container::samples_iterator samp_it = samples->begin();
00309
00310 for (; sit != send; (!ret? sit++: sit)) {
00311
00312
00313
00314
00315
00316
00317
00318
00319 string str = *sit;
00320 size_t pos = 0;
00321 while (pos < str.length() && isspace(str[pos]))
00322 ++pos;
00323
00324 if (pos == str.length() || !isxdigit(str[pos])) {
00325 if (do_output) {
00326 *sit = annotation_fill + str;
00327 continue;
00328 }
00329 }
00330
00331 while (pos < str.length() && isxdigit(str[pos]))
00332 ++pos;
00333
00334 if (pos == str.length() || (!isspace(str[pos]) && str[pos] != ':')) {
00335 if (do_output) {
00336 *sit = annotation_fill + str;
00337 continue;
00338 }
00339 }
00340
00341 if (is_symbol_line(str, pos)) {
00342
00343 last_symbol = find_symbol(app_name, str, vma_adj);
00344 last_symbol_vma = strtoull(str.c_str(), NULL, 16) - vma_adj;
00345
00346
00347
00348
00349
00350 if (find(symbols.begin(), symbols.end(), last_symbol)
00351 != symbols.end())
00352 do_output = true;
00353 else
00354 do_output = false;
00355
00356 if (do_output) {
00357 *sit += symbol_annotation(last_symbol);
00358
00359
00360
00361 samp_it = samples->begin(last_symbol);
00362 }
00363 } else {
00364
00365 if (do_output)
00366 ret = asm_list_annotation(last_symbol,
00367 last_symbol_vma,
00368 sit, samp_it,
00369 asm_lines, vma_adj);
00370 }
00371
00372 if (!do_output)
00373 *sit = "";
00374 }
00375 }
00376
00377
00378 void output_objdump_str_list(symbol_collection const & symbols,
00379 string const & app_name,
00380 list<string> & asm_lines)
00381 {
00382
00383 annotate_objdump_str_list(app_name, symbols, asm_lines);
00384
00385
00386 list<string>::iterator sit = asm_lines.begin();
00387 list<string>::iterator send = asm_lines.end();
00388 sit = asm_lines.begin();
00389 for (; sit != send; ++sit) {
00390 string str = *sit;
00391 if (str.length() != 0)
00392 cout << str << '\n';
00393 }
00394 }
00395
00396
00397 void do_one_output_objdump(symbol_collection const & symbols,
00398 string const & image_name, string const & app_name,
00399 bfd_vma start, bfd_vma end)
00400 {
00401 vector<string> args;
00402 list<string> asm_lines;
00403
00404 args.push_back("-d");
00405 args.push_back("--no-show-raw-insn");
00406 if (source)
00407 args.push_back("-S");
00408
00409 if (start || end != ~(bfd_vma)0) {
00410 ostringstream arg1, arg2;
00411 arg1 << "--start-address=" << start;
00412 arg2 << "--stop-address=" << end;
00413 args.push_back(arg1.str());
00414 args.push_back(arg2.str());
00415 }
00416
00417 if (!objdump_params.empty()) {
00418 for (size_t i = 0 ; i < objdump_params.size() ; ++i)
00419 args.push_back(objdump_params[i]);
00420 }
00421
00422 args.push_back(image_name);
00423 child_reader reader("objdump", args);
00424 if (reader.error()) {
00425 cerr << "An error occur during the execution of objdump:\n\n";
00426 cerr << reader.error_str() << endl;
00427 return;
00428 }
00429
00430
00431 string str;
00432 while (reader.getline(str))
00433 asm_lines.push_back(str);
00434
00435 output_objdump_str_list(symbols, app_name, asm_lines);
00436
00437
00438
00439
00440 ostringstream std_err;
00441 ostringstream std_out;
00442 reader.get_data(std_out, std_err);
00443 if (std_err.str().length()) {
00444 cerr << "An error occur during the execution of objdump:\n\n";
00445 cerr << std_err.str() << endl;
00446 return ;
00447 }
00448
00449
00450 reader.terminate_process();
00451
00452
00453
00454 if (reader.error()) {
00455 cerr << "An error occur during the execution of objdump:\n\n";
00456 cerr << reader.error_str() << endl;
00457 return;
00458 }
00459 }
00460
00461
00462 void output_objdump_asm(symbol_collection const & symbols,
00463 string const & app_name)
00464 {
00465 image_error error;
00466 string image =
00467 classes.extra_found_images.find_image_path(app_name, error,
00468 true);
00469
00470
00471
00472
00473
00474
00475
00476 size_t const max_objdump_exec = 50;
00477 if (symbols.size() <= max_objdump_exec || error != image_ok) {
00478 symbol_collection::const_iterator cit = symbols.begin();
00479 symbol_collection::const_iterator end = symbols.end();
00480 for (; cit != end; ++cit) {
00481 bfd_vma start = (*cit)->sample.vma;
00482 bfd_vma end = start + (*cit)->size;
00483 do_one_output_objdump(symbols, image, app_name,
00484 start, end);
00485 }
00486 } else {
00487 do_one_output_objdump(symbols, image,
00488 app_name, 0, ~bfd_vma(0));
00489 }
00490 }
00491
00492
00493 bool output_asm(string const & app_name)
00494 {
00495 profile_container::symbol_choice choice;
00496 choice.threshold = options::threshold;
00497 choice.image_name = app_name;
00498 choice.match_image = true;
00499 symbol_collection symbols = samples->select_symbols(choice);
00500
00501 if (!symbols.empty()) {
00502 sort_options options;
00503 options.add_sort_option(sort_options::sample);
00504 options.sort(symbols, false, false);
00505
00506 output_info(cout);
00507
00508 output_objdump_asm(symbols, app_name);
00509
00510 return true;
00511 }
00512
00513 return false;
00514 }
00515
00516
00517 string const source_line_annotation(debug_name_id filename, size_t linenr)
00518 {
00519 string str;
00520
00521 count_array_t counts = samples->samples_count(filename, linenr);
00522 if (!counts.zero()) {
00523 str += count_str(counts, samples->samples_count());
00524 for (size_t i = 1; i < nr_events; ++i)
00525 str += " ";
00526 str += " :";
00527 } else {
00528 str = annotation_fill;
00529 }
00530
00531 return str;
00532 }
00533
00534
00535 string source_symbol_annotation(debug_name_id filename, size_t linenr)
00536 {
00537 symbol_collection const symbols = samples->find_symbol(filename, linenr);
00538
00539 if (symbols.empty())
00540 return string();
00541
00542 string str = " " + begin_comment;
00543
00544 count_array_t counts;
00545 for (size_t i = 0; i < symbols.size(); ++i) {
00546 str += symbol_names.demangle(symbols[i]->name);
00547 if (symbols.size() == 1)
00548 str += " total: ";
00549 else
00550 str += " ";
00551 str += count_str(symbols[i]->sample.counts,
00552 samples->samples_count());
00553 if (symbols.size() != 1)
00554 str += ", ";
00555
00556 counts += symbols[i]->sample.counts;
00557 }
00558
00559 if (symbols.size() > 1)
00560 str += "total: " + count_str(counts, samples->samples_count());
00561 str += end_comment;
00562
00563 return str;
00564 }
00565
00566
00567 void output_per_file_info(ostream & out, debug_name_id filename,
00568 count_array_t const & total_file_count)
00569 {
00570 out << begin_comment << '\n'
00571 << in_comment << "Total samples for file : "
00572 << '"' << debug_names.name(filename) << '"'
00573 << '\n';
00574 out << in_comment << '\n' << in_comment
00575 << count_str(total_file_count, samples->samples_count())
00576 << '\n';
00577 out << end_comment << '\n' << '\n';
00578 }
00579
00580
00581 string const line0_info(debug_name_id filename)
00582 {
00583 string annotation = source_line_annotation(filename, 0);
00584 if (trim(annotation, " \t:").empty())
00585 return string();
00586
00587 string str = "<credited to line zero> ";
00588 str += annotation;
00589 return str;
00590 }
00591
00592
00593 void do_output_one_file(ostream & out, istream & in, debug_name_id filename,
00594 bool header)
00595 {
00596 count_array_t count = samples->samples_count(filename);
00597
00598 if (header) {
00599 output_per_file_info(out, filename, count);
00600 out << line0_info(filename) << '\n';
00601 }
00602
00603
00604 if (in) {
00605 string str;
00606
00607 for (size_t linenr = 1 ; getline(in, str) ; ++linenr) {
00608 out << source_line_annotation(filename, linenr) << str
00609 << source_symbol_annotation(filename, linenr)
00610 << '\n';
00611 }
00612
00613 } else {
00614
00615
00616
00617
00618 symbol_collection const symbols = samples->select_symbols(filename);
00619 for (size_t i = 0; i < symbols.size(); ++i)
00620 out << symbol_annotation(symbols[i]) << endl;
00621 }
00622
00623 if (!header) {
00624 output_per_file_info(out, filename, count);
00625 out << line0_info(filename) << '\n';
00626 }
00627 }
00628
00629
00630 void output_one_file(istream & in, debug_name_id filename,
00631 string const & source)
00632 {
00633 if (output_dir.empty()) {
00634 do_output_one_file(cout, in, filename, true);
00635 return;
00636 }
00637
00638 string const out_file = op_realpath(output_dir + source);
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650 if (out_file.find("/../") != string::npos) {
00651 if (in) {
00652 cerr << "refusing to create non-canonical filename "
00653 << out_file << endl;
00654 }
00655 return;
00656 } else if (!is_prefix(out_file, output_dir)) {
00657 if (in) {
00658 cerr << "refusing to create file " << out_file
00659 << " outside of output directory " << output_dir
00660 << endl;
00661 }
00662 return;
00663 }
00664
00665 if (is_files_identical(out_file, source)) {
00666 cerr << "input and output files are identical: "
00667 << out_file << endl;
00668 return;
00669 }
00670
00671 if (create_path(out_file.c_str())) {
00672 cerr << "unable to create file: "
00673 << '"' << op_dirname(out_file) << '"' << endl;
00674 return;
00675 }
00676
00677 ofstream out(out_file.c_str());
00678 if (!out) {
00679 cerr << "unable to open output file "
00680 << '"' << out_file << '"' << endl;
00681 } else {
00682 do_output_one_file(out, in, filename, false);
00683 output_info(out);
00684 }
00685 }
00686
00687
00688
00689 string const locate_source_file(debug_name_id filename_id)
00690 {
00691 string const origfile = debug_names.name(filename_id);
00692 string file = origfile;
00693
00694 if (file.empty())
00695 return file;
00696
00697
00698 if (file[0] == '/') {
00699 vector<string>::const_iterator cit = base_dirs.begin();
00700 vector<string>::const_iterator end = base_dirs.end();
00701 for (; cit != end; ++cit) {
00702 string path = op_realpath(*cit);
00703
00704 if (is_prefix(file, path)) {
00705 file = file.substr(path.length());
00706 break;
00707 }
00708 }
00709 }
00710
00711 vector<string>::const_iterator cit = search_dirs.begin();
00712 vector<string>::const_iterator end = search_dirs.end();
00713
00714 for (; cit != end; ++cit) {
00715 string const absfile = op_realpath(*cit + "/" + file);
00716
00717 if (op_file_readable(absfile))
00718 return absfile;
00719 }
00720
00721
00722
00723
00724
00725 return op_realpath(origfile);
00726 }
00727
00728
00729 void output_source(path_filter const & filter)
00730 {
00731 bool const separate_file = !output_dir.empty();
00732
00733 if (!separate_file)
00734 output_info(cout);
00735
00736 vector<debug_name_id> filenames =
00737 samples->select_filename(options::threshold);
00738
00739 for (size_t i = 0 ; i < filenames.size() ; ++i) {
00740 string const & source = locate_source_file(filenames[i]);
00741
00742 if (!filter.match(source))
00743 continue;
00744
00745 ifstream in(source.c_str());
00746
00747
00748
00749
00750
00751 if (!in && source.length()) {
00752 cerr << "opannotate (warning): unable to open for "
00753 "reading: " << source << endl;
00754 }
00755
00756 if (source.length())
00757 output_one_file(in, filenames[i], source);
00758 }
00759 }
00760
00761
00762 bool annotate_source(list<string> const & images)
00763 {
00764 annotation_fill = get_annotation_fill();
00765
00766 if (!output_dir.empty()) {
00767
00768 if (create_path(output_dir.c_str())) {
00769 cerr << "unable to create " << output_dir
00770 << " directory: " << endl;
00771 return false;
00772 }
00773
00774
00775 output_dir = op_realpath(output_dir);
00776 if (output_dir.length() &&
00777 output_dir[output_dir.length() - 1] != '/')
00778 output_dir += '/';
00779
00780
00781 if (output_dir == "/") {
00782 cerr << "Output path of / would over-write the "
00783 "source files" << endl;
00784 return false;
00785 }
00786 }
00787
00788 if (assembly) {
00789 bool some_output = false;
00790
00791 list<string>::const_iterator it = images.begin();
00792 list<string>::const_iterator const end = images.end();
00793
00794 for (; it != end; ++it) {
00795 if (output_asm(*it))
00796 some_output = true;
00797 }
00798
00799 if (!some_output) {
00800
00801
00802 cerr << "selected image set doesn't contain any of "
00803 << "the selected symbol\n";
00804 }
00805 } else {
00806 output_source(file_filter);
00807 }
00808
00809 return true;
00810 }
00811
00812
00813 int opannotate(options::spec const & spec)
00814 {
00815 handle_options(spec);
00816
00817 nr_events = classes.v.size();
00818
00819 samples.reset(new profile_container(true, true,
00820 classes.extra_found_images));
00821
00822 list<string> images;
00823
00824 list<inverted_profile> iprofiles = invert_profiles(classes);
00825
00826 report_image_errors(iprofiles, classes.extra_found_images);
00827
00828 list<inverted_profile>::iterator it = iprofiles.begin();
00829 list<inverted_profile>::iterator const end = iprofiles.end();
00830
00831 bool debug_info = false;
00832 for (; it != end; ++it) {
00833 bool tmp = false;
00834 populate_for_image(*samples, *it,
00835 options::symbol_filter, &tmp);
00836 images.push_back(it->image);
00837 if (tmp)
00838 debug_info = true;
00839 }
00840
00841 if (!debug_info && !options::assembly) {
00842 cerr << "opannotate (warning): no debug information available for binary "
00843 << it->image << ", and --assembly not requested\n";
00844 }
00845
00846 annotate_source(images);
00847
00848 return 0;
00849 }
00850
00851 }
00852
00853
00854 int main(int argc, char const * argv[])
00855 {
00856
00857 for (int i = 0 ; i < argc ; ++i)
00858 cmdline += string(argv[i]) + " ";
00859
00860 return run_pp_tool(argc, argv, opannotate);
00861 }