253 lines
6.4 KiB
C++
253 lines
6.4 KiB
C++
/**
|
|
* @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;
|
|
}
|