| /** |
| * @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.cpu; |
| } |
| |
| 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; |
| } |