/** * @file opreport_options.cpp * Options for opreport tool * * @remark Copyright 2003 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie */ #include <vector> #include <list> #include <iostream> #include <algorithm> #include <iterator> #include <fstream> #include "profile_spec.h" #include "arrange_profiles.h" #include "opreport_options.h" #include "popt_options.h" #include "string_filter.h" #include "file_manip.h" #include "xml_output.h" #include "xml_utils.h" #include "cverb.h" using namespace std; profile_classes classes; profile_classes classes2; namespace options { demangle_type demangle = dmt_normal; bool symbols; bool callgraph; bool debug_info; bool details; bool exclude_dependent; string_filter symbol_filter; sort_options sort_by; merge_option merge_by; bool show_header = true; bool long_filenames; bool show_address; bool accumulated; bool reverse_sort; bool global_percent; bool xml; string xml_options; } namespace { string outfile; vector<string> mergespec; vector<string> sort; vector<string> exclude_symbols; vector<string> include_symbols; string demangle_option = "normal"; popt::option options_array[] = { popt::option(options::callgraph, "callgraph", 'c', "show call graph"), popt::option(options::details, "details", 'd', "output detailed samples for each symbol"), popt::option(options::symbols, "symbols", 'l', "list all symbols"), popt::option(outfile, "output-file", 'o', "output to the given filename", "file"), popt::option(sort, "sort", 's', "sort by", "sample,image,app-name,symbol,debug,vma"), popt::option(options::reverse_sort, "reverse-sort", 'r', "use reverse sort"), popt::option(mergespec, "merge", 'm', "comma separated list", "cpu,lib,tid,tgid,unitmask,all"), popt::option(options::exclude_dependent, "exclude-dependent", 'x', "exclude libs, kernel, and module samples for applications"), popt::option(exclude_symbols, "exclude-symbols", 'e', "exclude these comma separated symbols", "symbols"), popt::option(include_symbols, "include-symbols", 'i', "include these comma separated symbols", "symbols"), popt::option(options::threshold_opt, "threshold", 't', "minimum percentage needed to produce output", "percent"), popt::option(demangle_option, "demangle", 'D', "demangle GNU C++ symbol names (default normal)", "none|normal|smart"), // PP:5 popt::option(options::debug_info, "debug-info", 'g', "add source file and line number to output"), popt::option(options::show_header, "no-header", 'n', "remove all headers from output"), popt::option(options::show_address, "show-address", 'w', "show VMA address of each symbol"), popt::option(options::long_filenames, "long-filenames", 'f', "show the full path of filenames"), popt::option(options::accumulated, "accumulated", 'a', "percentage field show accumulated count"), popt::option(options::global_percent, "global-percent", '%', "percentage are not relative to symbol count or image " "count but total sample count"), popt::option(options::xml, "xml", 'X', "XML output"), }; void handle_sort_option() { if (options::xml && !sort.empty()) { cerr << "warning: sort options ignored because they " << "are incompatible with --xml" << endl; // don't allow any other sorting, except the default below, // to mess up symbol traversal for XML sort.clear(); } if (sort.empty() || options::xml) { // PP:5.14 sort default to sample if (options::xml) { // implicitly sort by app-name,image so that in the // symbol traversal all library module symbols are // grouped together with their application sort.push_back("app-name"); sort.push_back("image"); } else sort.push_back("sample"); } vector<string>::const_iterator cit = sort.begin(); vector<string>::const_iterator end = sort.end(); for (; cit != end; ++cit) options::sort_by.add_sort_option(*cit); } void handle_output_file() { if (outfile.empty()) return; static ofstream os(outfile.c_str()); if (!os) { cerr << "Couldn't open \"" << outfile << "\" for writing." << endl; exit(EXIT_FAILURE); } cout.rdbuf(os.rdbuf()); } /// Check incompatible or meaningless options. void check_options(bool diff) { using namespace options; bool do_exit = false; if (callgraph) { symbols = true; if (details) { cerr << "--callgraph is incompatible with --details" << endl; do_exit = true; } if (diff) { cerr << "differential profiles are incompatible with --callgraph" << endl; do_exit = true; } } if (xml) { if (accumulated) { cerr << "--accumulated is incompatible with --xml" << endl; do_exit = true; } if (global_percent) { cerr << "--global_percent is incompatible with --xml" << endl; do_exit = true; } } if (details && diff) { cerr << "differential profiles are incompatible with --details" << endl; do_exit = true; } if (!symbols) { if (diff) { cerr << "different profiles are meaningless " "without --symbols" << endl; do_exit = true; } if (show_address) { cerr << "--show-address is meaningless " "without --symbols" << endl; do_exit = true; } if (debug_info || accumulated) { cerr << "--debug-info and --accumulated are " << "meaningless without --symbols" << endl; do_exit = true; } if (!exclude_symbols.empty() || !include_symbols.empty()) { cerr << "--exclude-symbols and --include-symbols are " << "meaningless without --symbols" << endl; do_exit = true; } if (find(sort_by.options.begin(), sort_by.options.end(), sort_options::vma) != sort_by.options.end()) { cerr << "--sort=vma is " << "meaningless without --symbols" << endl; do_exit = true; } } if (global_percent && symbols && !(details || callgraph)) { cerr << "--global-percent is meaningless with --symbols " "and without --details or --callgraph" << endl; do_exit = true; } if (do_exit) exit(EXIT_FAILURE); } /// process a spec into classes void process_spec(profile_classes & classes, list<string> const & spec) { using namespace options; copy(spec.begin(), spec.end(), ostream_iterator<string>(cverb << vsfile, " ")); cverb << vsfile << "\n\n"; profile_spec const pspec = profile_spec::create(spec, options::image_path, options::root_path); list<string> sample_files = pspec.generate_file_list(exclude_dependent, !options::callgraph); cverb << vsfile << "Archive: " << pspec.get_archive_path() << endl; cverb << vsfile << "Matched sample files: " << sample_files.size() << endl; copy(sample_files.begin(), sample_files.end(), ostream_iterator<string>(cverb << vsfile, "\n")); classes = arrange_profiles(sample_files, merge_by, pspec.extra_found_images); cverb << vsfile << "profile_classes:\n" << classes << endl; if (classes.v.empty()) { cerr << "error: no sample files found: profile specification " "too strict ?" << endl; exit(EXIT_FAILURE); } } } // namespace anon void handle_options(options::spec const & spec) { using namespace options; if (details) { symbols = true; show_address = true; } if (options::xml) { if (spec.common.size() != 0) xml_utils::add_option(SESSION, spec.common); if (debug_info) xml_utils::add_option(DEBUG_INFO, true); if (details) xml_utils::add_option(DETAILS, true); if (!image_path.empty()) xml_utils::add_option(IMAGE_PATH, image_path); if (!mergespec.empty()) xml_utils::add_option(MERGE, mergespec); if (exclude_dependent) xml_utils::add_option(EXCLUDE_DEPENDENT, true); if (!exclude_symbols.empty()) xml_utils::add_option(EXCLUDE_SYMBOLS, exclude_symbols); if (!include_symbols.empty()) xml_utils::add_option(INCLUDE_SYMBOLS, include_symbols); } handle_sort_option(); merge_by = handle_merge_option(mergespec, true, exclude_dependent); handle_output_file(); demangle = handle_demangle_option(demangle_option); check_options(spec.first.size()); symbol_filter = string_filter(include_symbols, exclude_symbols); if (!spec.first.size()) { process_spec(classes, spec.common); } else { if (options::xml) { cerr << "differential profiles are incompatible with --xml" << endl; exit(EXIT_FAILURE); } cverb << vsfile << "profile spec 1:" << endl; process_spec(classes, spec.first); cverb << vsfile << "profile spec 2:" << endl; process_spec(classes2, spec.second); if (!classes.matches(classes2)) { cerr << "profile classes are incompatible" << endl; exit(EXIT_FAILURE); } } }