Mike Dodd | 8cfa702 | 2010-11-17 11:12:26 -0800 | [diff] [blame] | 1 | /** |
| 2 | * @file opreport_options.cpp |
| 3 | * Options for opreport tool |
| 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 <vector> |
| 13 | #include <list> |
| 14 | #include <iostream> |
| 15 | #include <algorithm> |
| 16 | #include <iterator> |
| 17 | #include <fstream> |
| 18 | |
| 19 | #include "profile_spec.h" |
| 20 | #include "arrange_profiles.h" |
| 21 | #include "opreport_options.h" |
| 22 | #include "popt_options.h" |
| 23 | #include "string_filter.h" |
| 24 | #include "file_manip.h" |
| 25 | #include "xml_output.h" |
| 26 | #include "xml_utils.h" |
| 27 | #include "cverb.h" |
| 28 | |
| 29 | using namespace std; |
| 30 | |
| 31 | profile_classes classes; |
| 32 | profile_classes classes2; |
| 33 | |
| 34 | namespace options { |
| 35 | demangle_type demangle = dmt_normal; |
| 36 | bool symbols; |
| 37 | bool callgraph; |
| 38 | bool debug_info; |
| 39 | bool details; |
| 40 | bool exclude_dependent; |
| 41 | string_filter symbol_filter; |
| 42 | sort_options sort_by; |
| 43 | merge_option merge_by; |
| 44 | bool show_header = true; |
| 45 | bool long_filenames; |
| 46 | bool show_address; |
| 47 | bool accumulated; |
| 48 | bool reverse_sort; |
| 49 | bool global_percent; |
| 50 | bool xml; |
| 51 | string xml_options; |
| 52 | } |
| 53 | |
| 54 | |
| 55 | namespace { |
| 56 | |
| 57 | string outfile; |
| 58 | vector<string> mergespec; |
| 59 | vector<string> sort; |
| 60 | vector<string> exclude_symbols; |
| 61 | vector<string> include_symbols; |
| 62 | string demangle_option = "normal"; |
| 63 | |
| 64 | popt::option options_array[] = { |
| 65 | popt::option(options::callgraph, "callgraph", 'c', |
| 66 | "show call graph"), |
| 67 | popt::option(options::details, "details", 'd', |
| 68 | "output detailed samples for each symbol"), |
| 69 | popt::option(options::symbols, "symbols", 'l', |
| 70 | "list all symbols"), |
| 71 | |
| 72 | popt::option(outfile, "output-file", 'o', |
| 73 | "output to the given filename", "file"), |
| 74 | |
| 75 | popt::option(sort, "sort", 's', |
| 76 | "sort by", "sample,image,app-name,symbol,debug,vma"), |
| 77 | popt::option(options::reverse_sort, "reverse-sort", 'r', |
| 78 | "use reverse sort"), |
| 79 | popt::option(mergespec, "merge", 'm', |
| 80 | "comma separated list", "cpu,lib,tid,tgid,unitmask,all"), |
| 81 | popt::option(options::exclude_dependent, "exclude-dependent", 'x', |
| 82 | "exclude libs, kernel, and module samples for applications"), |
| 83 | popt::option(exclude_symbols, "exclude-symbols", 'e', |
| 84 | "exclude these comma separated symbols", "symbols"), |
| 85 | popt::option(include_symbols, "include-symbols", 'i', |
| 86 | "include these comma separated symbols", "symbols"), |
| 87 | popt::option(options::threshold_opt, "threshold", 't', |
| 88 | "minimum percentage needed to produce output", |
| 89 | "percent"), |
| 90 | |
| 91 | popt::option(demangle_option, "demangle", 'D', |
| 92 | "demangle GNU C++ symbol names (default normal)", |
| 93 | "none|normal|smart"), |
| 94 | // PP:5 |
| 95 | popt::option(options::debug_info, "debug-info", 'g', |
| 96 | "add source file and line number to output"), |
| 97 | popt::option(options::show_header, "no-header", 'n', |
| 98 | "remove all headers from output"), |
| 99 | popt::option(options::show_address, "show-address", 'w', |
| 100 | "show VMA address of each symbol"), |
| 101 | popt::option(options::long_filenames, "long-filenames", 'f', |
| 102 | "show the full path of filenames"), |
| 103 | popt::option(options::accumulated, "accumulated", 'a', |
| 104 | "percentage field show accumulated count"), |
| 105 | popt::option(options::global_percent, "global-percent", '%', |
| 106 | "percentage are not relative to symbol count or image " |
| 107 | "count but total sample count"), |
| 108 | |
| 109 | popt::option(options::xml, "xml", 'X', |
| 110 | "XML output"), |
| 111 | |
| 112 | }; |
| 113 | |
| 114 | |
| 115 | void handle_sort_option() |
| 116 | { |
| 117 | if (options::xml && !sort.empty()) { |
| 118 | cerr << "warning: sort options ignored because they " |
| 119 | << "are incompatible with --xml" << endl; |
| 120 | // don't allow any other sorting, except the default below, |
| 121 | // to mess up symbol traversal for XML |
| 122 | sort.clear(); |
| 123 | } |
| 124 | |
| 125 | if (sort.empty() || options::xml) { |
| 126 | // PP:5.14 sort default to sample |
| 127 | if (options::xml) { |
| 128 | // implicitly sort by app-name,image so that in the |
| 129 | // symbol traversal all library module symbols are |
| 130 | // grouped together with their application |
| 131 | sort.push_back("app-name"); |
| 132 | sort.push_back("image"); |
| 133 | } else |
| 134 | sort.push_back("sample"); |
| 135 | } |
| 136 | |
| 137 | vector<string>::const_iterator cit = sort.begin(); |
| 138 | vector<string>::const_iterator end = sort.end(); |
| 139 | |
| 140 | for (; cit != end; ++cit) |
| 141 | options::sort_by.add_sort_option(*cit); |
| 142 | } |
| 143 | |
| 144 | |
| 145 | void handle_output_file() |
| 146 | { |
| 147 | if (outfile.empty()) |
| 148 | return; |
| 149 | |
| 150 | static ofstream os(outfile.c_str()); |
| 151 | if (!os) { |
| 152 | cerr << "Couldn't open \"" << outfile |
| 153 | << "\" for writing." << endl; |
| 154 | exit(EXIT_FAILURE); |
| 155 | } |
| 156 | |
| 157 | cout.rdbuf(os.rdbuf()); |
| 158 | } |
| 159 | |
| 160 | |
| 161 | /// Check incompatible or meaningless options. |
| 162 | void check_options(bool diff) |
| 163 | { |
| 164 | using namespace options; |
| 165 | |
| 166 | bool do_exit = false; |
| 167 | |
| 168 | if (callgraph) { |
| 169 | symbols = true; |
| 170 | if (details) { |
| 171 | cerr << "--callgraph is incompatible with --details" << endl; |
| 172 | do_exit = true; |
| 173 | } |
| 174 | |
| 175 | if (diff) { |
| 176 | cerr << "differential profiles are incompatible with --callgraph" << endl; |
| 177 | do_exit = true; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | if (xml) { |
| 182 | if (accumulated) { |
| 183 | cerr << "--accumulated is incompatible with --xml" << endl; |
| 184 | do_exit = true; |
| 185 | } |
| 186 | |
| 187 | if (global_percent) { |
| 188 | cerr << "--global_percent is incompatible with --xml" << endl; |
| 189 | do_exit = true; |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | |
| 194 | if (details && diff) { |
| 195 | cerr << "differential profiles are incompatible with --details" << endl; |
| 196 | do_exit = true; |
| 197 | } |
| 198 | |
| 199 | if (!symbols) { |
| 200 | if (diff) { |
| 201 | cerr << "different profiles are meaningless " |
| 202 | "without --symbols" << endl; |
| 203 | do_exit = true; |
| 204 | } |
| 205 | |
| 206 | if (show_address) { |
| 207 | cerr << "--show-address is meaningless " |
| 208 | "without --symbols" << endl; |
| 209 | do_exit = true; |
| 210 | } |
| 211 | |
| 212 | if (debug_info || accumulated) { |
| 213 | cerr << "--debug-info and --accumulated are " |
| 214 | << "meaningless without --symbols" << endl; |
| 215 | do_exit = true; |
| 216 | } |
| 217 | |
| 218 | if (!exclude_symbols.empty() || !include_symbols.empty()) { |
| 219 | cerr << "--exclude-symbols and --include-symbols are " |
| 220 | << "meaningless without --symbols" << endl; |
| 221 | do_exit = true; |
| 222 | } |
| 223 | |
| 224 | if (find(sort_by.options.begin(), sort_by.options.end(), |
| 225 | sort_options::vma) != sort_by.options.end()) { |
| 226 | cerr << "--sort=vma is " |
| 227 | << "meaningless without --symbols" << endl; |
| 228 | do_exit = true; |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | if (global_percent && symbols && !(details || callgraph)) { |
| 233 | cerr << "--global-percent is meaningless with --symbols " |
| 234 | "and without --details or --callgraph" << endl; |
| 235 | do_exit = true; |
| 236 | } |
| 237 | |
| 238 | if (do_exit) |
| 239 | exit(EXIT_FAILURE); |
| 240 | } |
| 241 | |
| 242 | |
| 243 | /// process a spec into classes |
| 244 | void process_spec(profile_classes & classes, list<string> const & spec) |
| 245 | { |
| 246 | using namespace options; |
| 247 | |
| 248 | copy(spec.begin(), spec.end(), |
| 249 | ostream_iterator<string>(cverb << vsfile, " ")); |
| 250 | cverb << vsfile << "\n\n"; |
| 251 | |
| 252 | profile_spec const pspec = |
| 253 | profile_spec::create(spec, options::image_path, |
| 254 | options::root_path); |
| 255 | |
| 256 | list<string> sample_files = pspec.generate_file_list(exclude_dependent, |
| 257 | !options::callgraph); |
| 258 | |
| 259 | cverb << vsfile << "Archive: " << pspec.get_archive_path() << endl; |
| 260 | |
| 261 | cverb << vsfile << "Matched sample files: " << sample_files.size() |
| 262 | << endl; |
| 263 | copy(sample_files.begin(), sample_files.end(), |
| 264 | ostream_iterator<string>(cverb << vsfile, "\n")); |
| 265 | |
| 266 | classes = arrange_profiles(sample_files, merge_by, |
| 267 | pspec.extra_found_images); |
| 268 | |
| 269 | cverb << vsfile << "profile_classes:\n" << classes << endl; |
| 270 | |
| 271 | if (classes.v.empty()) { |
| 272 | cerr << "error: no sample files found: profile specification " |
| 273 | "too strict ?" << endl; |
| 274 | exit(EXIT_FAILURE); |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | |
| 279 | } // namespace anon |
| 280 | |
| 281 | |
| 282 | void handle_options(options::spec const & spec) |
| 283 | { |
| 284 | using namespace options; |
| 285 | |
| 286 | if (details) { |
| 287 | symbols = true; |
| 288 | show_address = true; |
| 289 | } |
| 290 | |
| 291 | if (options::xml) { |
| 292 | if (spec.common.size() != 0) |
| 293 | xml_utils::add_option(SESSION, spec.common); |
| 294 | if (debug_info) |
| 295 | xml_utils::add_option(DEBUG_INFO, true); |
| 296 | if (details) |
| 297 | xml_utils::add_option(DETAILS, true); |
| 298 | if (!image_path.empty()) |
| 299 | xml_utils::add_option(IMAGE_PATH, image_path); |
| 300 | if (!mergespec.empty()) |
| 301 | xml_utils::add_option(MERGE, mergespec); |
| 302 | if (exclude_dependent) |
| 303 | xml_utils::add_option(EXCLUDE_DEPENDENT, true); |
| 304 | if (!exclude_symbols.empty()) |
| 305 | xml_utils::add_option(EXCLUDE_SYMBOLS, exclude_symbols); |
| 306 | if (!include_symbols.empty()) |
| 307 | xml_utils::add_option(INCLUDE_SYMBOLS, include_symbols); |
| 308 | } |
| 309 | |
| 310 | handle_sort_option(); |
| 311 | merge_by = handle_merge_option(mergespec, true, exclude_dependent); |
| 312 | handle_output_file(); |
| 313 | demangle = handle_demangle_option(demangle_option); |
| 314 | check_options(spec.first.size()); |
| 315 | |
| 316 | symbol_filter = string_filter(include_symbols, exclude_symbols); |
| 317 | |
| 318 | if (!spec.first.size()) { |
| 319 | process_spec(classes, spec.common); |
| 320 | } else { |
| 321 | if (options::xml) { |
| 322 | cerr << "differential profiles are incompatible with --xml" << endl; |
| 323 | exit(EXIT_FAILURE); |
| 324 | } |
| 325 | cverb << vsfile << "profile spec 1:" << endl; |
| 326 | process_spec(classes, spec.first); |
| 327 | cverb << vsfile << "profile spec 2:" << endl; |
| 328 | process_spec(classes2, spec.second); |
| 329 | |
| 330 | if (!classes.matches(classes2)) { |
| 331 | cerr << "profile classes are incompatible" << endl; |
| 332 | exit(EXIT_FAILURE); |
| 333 | } |
| 334 | } |
| 335 | } |