/** * @file parse_filename.cpp * Split a sample filename into its constituent parts * * @remark Copyright 2003 OProfile authors * @remark Read the file COPYING * * @author Philippe Elie */ #include <stdexcept> #include <vector> #include <string> #include <iostream> #include <sys/stat.h> #include "parse_filename.h" #include "file_manip.h" #include "string_manip.h" #include "locate_images.h" using namespace std; namespace { // PP:3.19 event_name.count.unitmask.tgid.tid.cpu parsed_filename parse_event_spec(string const & event_spec) { typedef vector<string> parts_type; typedef parts_type::size_type size_type; size_type const nr_parts = 6; parts_type parts = separate_token(event_spec, '.'); if (parts.size() != nr_parts) { throw invalid_argument("parse_event_spec(): bad event specification: " + event_spec); } for (size_type i = 0; i < nr_parts ; ++i) { if (parts[i].empty()) { throw invalid_argument("parse_event_spec(): bad event specification: " + event_spec); } } parsed_filename result; size_type i = 0; result.event = parts[i++]; result.count = parts[i++]; result.unitmask = parts[i++]; result.tgid = parts[i++]; result.tid = parts[i++]; result.cpu = parts[i++]; return result; } /** * @param component path component * * remove from path_component all directory left to {root}, {kern} or {anon} */ void remove_base_dir(vector<string> & path) { vector<string>::iterator it; for (it = path.begin(); it != path.end(); ++it) { if (*it == "{root}" || *it == "{kern}" || *it == "{anon}") break; } path.erase(path.begin(), it); } /// Handle an anon region. Pretty print the details. /// The second argument is the anon portion of the path which will /// contain extra details such as the anon region name (unknown, vdso, heap etc.) string const parse_anon(string const & str, string const & str2) { string name = str2; // Get rid of "{anon: name.erase(0, 6); // Catch the case where we end up with an empty string. This should // never happen, except where things have gone awfully bad with profile // data collection, resulting in one or more bogus sample files. if(0 == name.size()) throw invalid_argument("parse_anon() invalid name: " + str2 + "\n" + "This error indicates your sample data is suspect. It is " + "recommended you do a --reset and collect new profile data."); // Get rid of the trailing '}' name.erase(name.size() - 1, 1); vector<string> parts = separate_token(str, '.'); if (parts.size() != 3) throw invalid_argument("parse_anon() invalid name: " + str); string ret = name +" (tgid:"; ret += parts[0] + " range:" + parts[1] + "-" + parts[2] + ")"; return ret; } } // anonymous namespace /* * valid filename are variations on: * * {kern}/name/event_spec * {root}/path/to/bin/{dep}/{root}/path/to/bin/event_spec * {root}/path/to/bin/{dep}/{anon:anon}/pid.start.end/event_spec * {root}/path/to/bin/{dep}/{anon:[vdso]}/pid.start.end/event_spec * {root}/path/to/bin/{dep}/{kern}/name/event_spec * {root}/path/to/bin/{dep}/{root}/path/to/bin/{cg}/{root}/path/to/bin/event_spec * * where /name/ denote a unique path component */ parsed_filename parse_filename(string const & filename, extra_images const & extra_found_images) { struct stat st; string::size_type pos = filename.find_last_of('/'); if (pos == string::npos) { throw invalid_argument("parse_filename() invalid filename: " + filename); } string event_spec = filename.substr(pos + 1); string filename_spec = filename.substr(0, pos); parsed_filename result = parse_event_spec(event_spec); result.filename = filename; vector<string> path = separate_token(filename_spec, '/'); remove_base_dir(path); // pp_interface PP:3.19 to PP:3.23 path must start either with {root} // or {kern} and we must found at least 2 component, remove_base_dir() // return an empty path if {root} or {kern} are not found if (path.size() < 2) { throw invalid_argument("parse_filename() invalid filename: " + filename); } size_t i; for (i = 1 ; i < path.size() ; ++i) { if (path[i] == "{dep}") break; result.image += "/" + path[i]; } if (i == path.size()) { throw invalid_argument("parse_filename() invalid filename: " + filename); } // skip "{dep}" ++i; // PP:3.19 {dep}/ must be followed by {kern}/, {root}/ or {anon}/ if (path[i] != "{kern}" && path[i] != "{root}" && path[i].find("{anon", 0) != 0) { throw invalid_argument("parse_filename() invalid filename: " + filename); } bool anon = path[i].find("{anon:", 0) == 0; // skip "{root}", "{kern}" or "{anon:.*}" ++i; for (; i < path.size(); ++i) { if (path[i] == "{cg}") break; if (anon) { pos = filename_spec.rfind('.'); pos = filename_spec.rfind('.', pos-1); if (pos == string::npos) { throw invalid_argument("parse_filename() pid.addr.addr name expected: " + filename_spec); } string jitdump = filename_spec.substr(0, pos) + ".jo"; // if a jitdump file exists, we point to this file if (!stat(jitdump.c_str(), &st)) { // later code assumes an optional prefix path // is stripped from the lib_image. result.lib_image = extra_found_images.strip_path_prefix(jitdump); result.jit_dumpfile_exists = true; } else { result.lib_image = parse_anon(path[i], path[i - 1]); } i++; break; } else { result.lib_image += "/" + path[i]; } } if (i == path.size()) return result; // skip "{cg}" ++i; if (i == path.size() || (path[i] != "{kern}" && path[i] != "{root}" && path[i].find("{anon", 0) != 0)) { throw invalid_argument("parse_filename() invalid filename: " + filename); } // skip "{root}", "{kern}" or "{anon}" anon = (path[i].find("{anon", 0) == 0); ++i; if (anon) { result.cg_image = parse_anon(path[i], path[i - 1]); i++; } else { for (; i < path.size(); ++i) result.cg_image += "/" + path[i]; } return result; } bool parsed_filename::profile_spec_equal(parsed_filename const & parsed) { return event == parsed.event && count == parsed.count && unitmask == parsed.unitmask && tgid == parsed.tgid && tid == parsed.tid && cpu == parsed.tid; } ostream & operator<<(ostream & out, parsed_filename const & data) { out << data.filename << endl; out << data.image << " " << data.lib_image << " " << data.event << " " << data.count << " " << data.unitmask << " " << data.tgid << " " << data.tid << " " << data.cpu << endl; return out; }