profile_spec.cpp revision cc2ee177dbb3befca43e36cfc56778b006c3d050
1/** 2 * @file profile_spec.cpp 3 * Contains a PP profile specification 4 * 5 * @remark Copyright 2003 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author Philippe Elie 9 */ 10 11#include <algorithm> 12#include <set> 13#include <sstream> 14#include <iterator> 15#include <iostream> 16 17#include "file_manip.h" 18#include "op_config.h" 19#include "profile_spec.h" 20#include "string_manip.h" 21#include "glob_filter.h" 22#include "locate_images.h" 23#include "op_exception.h" 24 25using namespace std; 26 27namespace { 28 29// PP:3.7, full path, or relative path. If we can't find it, 30// we should maintain the original to maintain the wordexp etc. 31string const fixup_image_spec(string const & str, extra_images const & extra) 32{ 33 string dummy_archive_path; 34 // FIXME: what todo if an error in find_image_path() ? 35 image_error error; 36 return find_image_path(dummy_archive_path, str, extra, error); 37} 38 39 40void fixup_image_spec(vector<string> & images, extra_images const & extra) 41{ 42 vector<string>::iterator it = images.begin(); 43 vector<string>::iterator const end = images.end(); 44 45 for (; it != end; ++it) { 46 *it = fixup_image_spec(*it, extra); 47 } 48} 49 50} // anon namespace 51 52 53profile_spec::profile_spec(extra_images const & extra) 54 : extra(extra) 55{ 56 parse_table["archive"] = &profile_spec::parse_archive_path; 57 parse_table["session"] = &profile_spec::parse_session; 58 parse_table["session-exclude"] = 59 &profile_spec::parse_session_exclude; 60 parse_table["image"] = &profile_spec::parse_image; 61 parse_table["image-exclude"] = &profile_spec::parse_image_exclude; 62 parse_table["lib-image"] = &profile_spec::parse_lib_image; 63 parse_table["event"] = &profile_spec::parse_event; 64 parse_table["count"] = &profile_spec::parse_count; 65 parse_table["unit-mask"] = &profile_spec::parse_unitmask; 66 parse_table["tid"] = &profile_spec::parse_tid; 67 parse_table["tgid"] = &profile_spec::parse_tgid; 68 parse_table["cpu"] = &profile_spec::parse_cpu; 69} 70 71 72void profile_spec::parse(string const & tag_value) 73{ 74 string value; 75 action_t action = get_handler(tag_value, value); 76 if (!action) { 77 throw invalid_argument("profile_spec::parse(): not " 78 "a valid tag \"" + tag_value + "\""); 79 } 80 81 (this->*action)(value); 82} 83 84 85bool profile_spec::is_valid_tag(string const & tag_value) 86{ 87 string value; 88 return get_handler(tag_value, value); 89} 90 91 92void profile_spec::set_image_or_lib_name(string const & str) 93{ 94 /* FIXME: what does spec say about this being allowed to be 95 * a comma list or not ? */ 96 image_or_lib_image.push_back(fixup_image_spec(str, extra)); 97} 98 99 100void profile_spec::parse_archive_path(string const & str) 101{ 102 archive_path = op_realpath(str); 103} 104 105 106string profile_spec::get_archive_path() const 107{ 108 return archive_path; 109} 110 111 112void profile_spec::parse_session(string const & str) 113{ 114 session = separate_token(str, ','); 115} 116 117 118void profile_spec::parse_session_exclude(string const & str) 119{ 120 session_exclude = separate_token(str, ','); 121} 122 123 124void profile_spec::parse_image(string const & str) 125{ 126 image = separate_token(str, ','); 127 fixup_image_spec(image, extra); 128} 129 130 131void profile_spec::parse_image_exclude(string const & str) 132{ 133 image_exclude = separate_token(str, ','); 134} 135 136 137void profile_spec::parse_lib_image(string const & str) 138{ 139 lib_image = separate_token(str, ','); 140 fixup_image_spec(image, extra); 141} 142 143 144void profile_spec::parse_event(string const & str) 145{ 146 event.set(str); 147} 148 149 150void profile_spec::parse_count(string const & str) 151{ 152 count.set(str); 153} 154 155 156void profile_spec::parse_unitmask(string const & str) 157{ 158 unitmask.set(str); 159} 160 161 162void profile_spec::parse_tid(string const & str) 163{ 164 tid.set(str); 165} 166 167 168void profile_spec::parse_tgid(string const & str) 169{ 170 tgid.set(str); 171} 172 173 174void profile_spec::parse_cpu(string const & str) 175{ 176 cpu.set(str); 177} 178 179 180profile_spec::action_t 181profile_spec::get_handler(string const & tag_value, string & value) 182{ 183 string::size_type pos = tag_value.find_first_of(':'); 184 if (pos == string::npos) { 185 return 0; 186 } 187 188 string tag(tag_value.substr(0, pos)); 189 value = tag_value.substr(pos + 1); 190 191 parse_table_t::const_iterator it = parse_table.find(tag); 192 if (it == parse_table.end()) { 193 return 0; 194 } 195 196 return it->second; 197} 198 199 200namespace { 201 202/// return true if the value from the profile spec may match the comma 203/// list 204template<typename T> 205bool comma_match(comma_list<T> const & cl, generic_spec<T> const & value) 206{ 207 // if the profile spec is "all" we match the sample file 208 if (!cl.is_set()) 209 return true; 210 211 // an "all" sample file should never match specified profile 212 // spec values 213 if (!value.is_set()) 214 return false; 215 216 // now match each profile spec value against the sample file 217 return cl.match(value.value()); 218} 219 220} 221 222 223bool profile_spec::match(filename_spec const & spec) const 224{ 225 bool matched_by_image_or_lib_image = false; 226 227 // PP:3.19 228 if (!image_or_lib_image.empty()) { 229 // Need the path search for the benefit of modules 230 // which have "/oprofile" or similar 231 string simage = fixup_image_spec(spec.image, extra); 232 string slib_image = fixup_image_spec(spec.lib_image, extra); 233 glob_filter filter(image_or_lib_image, image_exclude); 234 if (filter.match(simage) || filter.match(slib_image)) { 235 matched_by_image_or_lib_image = true; 236 } 237 } 238 239 if (!matched_by_image_or_lib_image) { 240 // PP:3.7 3.8 241 if (!image.empty()) { 242 glob_filter filter(image, image_exclude); 243 if (!filter.match(spec.image)) { 244 return false; 245 } 246 } else if (!image_or_lib_image.empty()) { 247 // image.empty() means match all except if user 248 // specified image_or_lib_image 249 return false; 250 } 251 252 // PP:3.9 3.10 253 if (!lib_image.empty()) { 254 glob_filter filter(lib_image, image_exclude); 255 if (!filter.match(spec.lib_image)) { 256 return false; 257 } 258 } else if (image.empty() && !image_or_lib_image.empty()) { 259 // lib_image empty means match all except if user 260 // specified image_or_lib_image *or* we already 261 // matched this spec through image 262 return false; 263 } 264 } 265 266 if (!matched_by_image_or_lib_image) { 267 // if we don't match by image_or_lib_image we must try to 268 // exclude from spec, exclusion from image_or_lib_image has 269 // been handled above 270 vector<string> empty; 271 glob_filter filter(empty, image_exclude); 272 if (!filter.match(spec.image)) { 273 return false; 274 } 275 if (!spec.lib_image.empty() && !filter.match(spec.lib_image)) { 276 return false; 277 } 278 } 279 280 if (!event.match(spec.event)) 281 return false; 282 283 if (!count.match(spec.count)) 284 return false; 285 286 if (!unitmask.match(spec.unitmask)) 287 return false; 288 289 if (!comma_match(cpu, spec.cpu)) 290 return false; 291 292 if (!comma_match(tid, spec.tid)) 293 return false; 294 295 if (!comma_match(tgid, spec.tgid)) 296 return false; 297 298 return true; 299} 300 301 302profile_spec profile_spec::create(list<string> const & args, 303 extra_images const & extra) 304{ 305 profile_spec spec(extra); 306 set<string> tag_seen; 307 308 list<string>::const_iterator it = args.begin(); 309 list<string>::const_iterator end = args.end(); 310 311 for (; it != end; ++it) { 312 if (spec.is_valid_tag(*it)) { 313 if (tag_seen.find(*it) != tag_seen.end()) { 314 throw op_runtime_error("tag specified " 315 "more than once: " + *it); 316 } 317 tag_seen.insert(*it); 318 spec.parse(*it); 319 } else { 320 string const file = op_realpath(*it); 321 spec.set_image_or_lib_name(file); 322 } 323 } 324 325 // PP:3.5 no session given means use the current session. 326 if (spec.session.empty()) { 327 spec.session.push_back("current"); 328 } 329 330 return spec; 331} 332 333namespace { 334 335vector<string> filter_session(vector<string> const & session, 336 vector<string> const & session_exclude) 337{ 338 vector<string> result(session); 339 340 if (result.empty()) { 341 result.push_back("current"); 342 } 343 344 for (size_t i = 0 ; i < session_exclude.size() ; ++i) { 345 // FIXME: would we use fnmatch on each item, are we allowed 346 // to --session=current* ? 347 vector<string>::iterator it = 348 find(result.begin(), result.end(), session_exclude[i]); 349 350 if (it != result.end()) { 351 result.erase(it); 352 } 353 } 354 355 return result; 356} 357 358 359bool valid_candidate(string const & base_dir, string const & filename, 360 profile_spec const & spec, bool exclude_dependent, 361 bool exclude_cg) 362{ 363 if (exclude_cg && filename.find("{cg}") != string::npos) 364 return false; 365 366 // strip out non sample files 367 string const & sub = filename.substr(base_dir.size(), string::npos); 368 if (!is_prefix(sub, "/{root}/") && !is_prefix(sub, "/{kern}/")) 369 return false; 370 371 filename_spec file_spec(filename); 372 if (spec.match(file_spec)) { 373 if (exclude_dependent && file_spec.is_dependent()) 374 return false; 375 return true; 376 } 377 378 return false; 379} 380 381} // anonymous namespace 382 383 384list<string> profile_spec::generate_file_list(bool exclude_dependent, 385 bool exclude_cg) const 386{ 387 // FIXME: isn't remove_duplicates faster than doing this, then copy() ? 388 set<string> unique_files; 389 390 vector<string> sessions = filter_session(session, session_exclude); 391 392 if (sessions.empty()) { 393 ostringstream os; 394 os << "No session given\n" 395 << "included session was:\n"; 396 copy(session.begin(), session.end(), 397 ostream_iterator<string>(os, "\n")); 398 os << "excluded session was:\n"; 399 copy(session_exclude.begin(), session_exclude.end(), 400 ostream_iterator<string>(os, "\n")); 401 throw invalid_argument(os.str()); 402 } 403 404 bool found_file = false; 405 406 vector<string>::const_iterator cit = sessions.begin(); 407 vector<string>::const_iterator end = sessions.end(); 408 409 for (; cit != end; ++cit) { 410 if (cit->empty()) 411 continue; 412 413 string base_dir; 414 if ((*cit)[0] != '.' && (*cit)[0] != '/') 415 base_dir = archive_path + OP_SAMPLES_DIR; 416 base_dir += *cit; 417 418 base_dir = op_realpath(base_dir); 419 420 list<string> files; 421 create_file_list(files, base_dir, "*", true); 422 423 if (!files.empty()) 424 found_file = true; 425 426 list<string>::const_iterator it = files.begin(); 427 list<string>::const_iterator fend = files.end(); 428 for (; it != fend; ++it) { 429 if (valid_candidate(base_dir, *it, *this, 430 exclude_dependent, exclude_cg)) { 431 unique_files.insert(*it); 432 } 433 } 434 } 435 436 if (!found_file) { 437 ostringstream os; 438 os << "No sample file found: try running opcontrol --dump\n" 439 << "or specify a session containing sample files\n"; 440 throw op_fatal_error(os.str()); 441 } 442 443 list<string> result; 444 copy(unique_files.begin(), unique_files.end(), back_inserter(result)); 445 446 return result; 447} 448