• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file parse_filename.cpp
3  * Split a sample filename into its constituent parts
4  *
5  * @remark Copyright 2003 OProfile authors
6  * @remark Read the file COPYING
7  *
8  * @author Philippe Elie
9  */
10 
11 #include <stdexcept>
12 #include <vector>
13 #include <string>
14 #include <iostream>
15 #include <sys/stat.h>
16 
17 #include "parse_filename.h"
18 #include "file_manip.h"
19 #include "string_manip.h"
20 #include "locate_images.h"
21 
22 using namespace std;
23 
24 namespace {
25 
26 // PP:3.19 event_name.count.unitmask.tgid.tid.cpu
parse_event_spec(string const & event_spec)27 parsed_filename parse_event_spec(string const & event_spec)
28 {
29 	typedef vector<string> parts_type;
30 	typedef parts_type::size_type size_type;
31 
32 	size_type const nr_parts = 6;
33 
34 	parts_type parts = separate_token(event_spec, '.');
35 
36 	if (parts.size() != nr_parts) {
37 		throw invalid_argument("parse_event_spec(): bad event specification: " + event_spec);
38 	}
39 
40 	for (size_type i = 0; i < nr_parts ; ++i) {
41 		if (parts[i].empty()) {
42 			throw invalid_argument("parse_event_spec(): bad event specification: " + event_spec);
43 		}
44 	}
45 
46 	parsed_filename result;
47 
48 	size_type i = 0;
49 	result.event = parts[i++];
50 	result.count = parts[i++];
51 	result.unitmask = parts[i++];
52 	result.tgid = parts[i++];
53 	result.tid = parts[i++];
54 	result.cpu = parts[i++];
55 
56 	return result;
57 }
58 
59 
60 /**
61  * @param component  path component
62  *
63  * remove from path_component all directory left to {root}, {kern} or {anon}
64  */
remove_base_dir(vector<string> & path)65 void remove_base_dir(vector<string> & path)
66 {
67 	vector<string>::iterator it;
68 	for (it = path.begin(); it != path.end(); ++it) {
69 		if (*it == "{root}" || *it == "{kern}"  || *it == "{anon}")
70 			break;
71 	}
72 
73 	path.erase(path.begin(), it);
74 }
75 
76 
77 /// Handle an anon region. Pretty print the details.
78 /// The second argument is the anon portion of the path which will
79 /// contain extra details such as the anon region name (unknown, vdso, heap etc.)
parse_anon(string const & str,string const & str2)80 string const parse_anon(string const & str, string const & str2)
81 {
82 	string name = str2;
83 	// Get rid of "{anon:
84 	name.erase(0, 6);
85 	// Catch the case where we end up with an empty string.  This should
86 	// never happen, except where things have gone awfully bad with profile
87 	// data collection, resulting in one or more bogus sample files.
88 	if(0 == name.size())
89 		throw invalid_argument("parse_anon() invalid name: " + str2 + "\n"
90 			+ "This error indicates your sample data is suspect. It is "
91 			+ "recommended you do a --reset and collect new profile data.");
92 	// Get rid of the trailing '}'
93 	name.erase(name.size() - 1, 1);
94 	vector<string> parts = separate_token(str, '.');
95 	if (parts.size() != 3)
96 		throw invalid_argument("parse_anon() invalid name: " + str);
97 
98 	string ret = name +" (tgid:";
99 	ret += parts[0] + " range:" + parts[1] + "-" + parts[2] + ")";
100 	return ret;
101 }
102 
103 
104 }  // anonymous namespace
105 
106 
107 /*
108  *  valid filename are variations on:
109  *
110  * {kern}/name/event_spec
111  * {root}/path/to/bin/{dep}/{root}/path/to/bin/event_spec
112  * {root}/path/to/bin/{dep}/{anon:anon}/pid.start.end/event_spec
113  * {root}/path/to/bin/{dep}/{anon:[vdso]}/pid.start.end/event_spec
114  * {root}/path/to/bin/{dep}/{kern}/name/event_spec
115  * {root}/path/to/bin/{dep}/{root}/path/to/bin/{cg}/{root}/path/to/bin/event_spec
116 
117  *
118  * where /name/ denote a unique path component
119  */
parse_filename(string const & filename,extra_images const & extra_found_images)120 parsed_filename parse_filename(string const & filename,
121 			       extra_images const & extra_found_images)
122 {
123 	struct stat st;
124 
125 	string::size_type pos = filename.find_last_of('/');
126 	if (pos == string::npos) {
127 		throw invalid_argument("parse_filename() invalid filename: " +
128 				       filename);
129 	}
130 	string event_spec = filename.substr(pos + 1);
131 	string filename_spec = filename.substr(0, pos);
132 
133 	parsed_filename result = parse_event_spec(event_spec);
134 
135 	result.filename = filename;
136 
137 	vector<string> path = separate_token(filename_spec, '/');
138 
139 	remove_base_dir(path);
140 
141 	// pp_interface PP:3.19 to PP:3.23 path must start either with {root}
142 	// or {kern} and we must found at least 2 component, remove_base_dir()
143 	// return an empty path if {root} or {kern} are not found
144 	if (path.size() < 2) {
145 		throw invalid_argument("parse_filename() invalid filename: " +
146 				       filename);
147 	}
148 
149 	size_t i;
150 	for (i = 1 ; i < path.size() ; ++i) {
151 		if (path[i] == "{dep}")
152 			break;
153 
154 		result.image += "/" + path[i];
155 	}
156 
157 	if (i == path.size()) {
158 		throw invalid_argument("parse_filename() invalid filename: " +
159 				       filename);
160 	}
161 
162 	// skip "{dep}"
163 	++i;
164 
165 	// PP:3.19 {dep}/ must be followed by {kern}/, {root}/ or {anon}/
166 	if (path[i] != "{kern}" && path[i] != "{root}" &&
167 	    path[i].find("{anon", 0) != 0) {
168 		throw invalid_argument("parse_filename() invalid filename: " +
169 				       filename);
170 	}
171 
172 	bool anon = path[i].find("{anon:", 0) == 0;
173 
174 	// skip "{root}", "{kern}" or "{anon:.*}"
175 	++i;
176 
177 	for (; i < path.size(); ++i) {
178 		if (path[i] == "{cg}")
179 			break;
180 
181 		if (anon) {
182 			pos = filename_spec.rfind('.');
183 			pos = filename_spec.rfind('.', pos-1);
184 			if (pos == string::npos) {
185 				throw invalid_argument("parse_filename() pid.addr.addr name expected: " +
186 						       filename_spec);
187 			}
188 			string jitdump = filename_spec.substr(0, pos) + ".jo";
189 			// if a jitdump file exists, we point to this file
190 			if (!stat(jitdump.c_str(), &st)) {
191 				// later code assumes an optional prefix path
192 				// is stripped from the lib_image.
193 				result.lib_image =
194 					extra_found_images.strip_path_prefix(jitdump);
195 				result.jit_dumpfile_exists = true;
196 			} else {
197 				result.lib_image =  parse_anon(path[i], path[i - 1]);
198 			}
199 			i++;
200 			break;
201 		} else {
202 			result.lib_image += "/" + path[i];
203 		}
204 	}
205 
206 	if (i == path.size())
207 		return result;
208 
209 	// skip "{cg}"
210 	++i;
211 	if (i == path.size() ||
212 	    (path[i] != "{kern}" && path[i] != "{root}" &&
213 	     path[i].find("{anon", 0) != 0)) {
214 		throw invalid_argument("parse_filename() invalid filename: "
215 		                       + filename);
216 	}
217 
218 	// skip "{root}", "{kern}" or "{anon}"
219 	anon = (path[i].find("{anon", 0) == 0);
220 	++i;
221 
222 	if (anon) {
223 		result.cg_image = parse_anon(path[i], path[i - 1]);
224 		i++;
225 	} else {
226 		for (; i < path.size(); ++i)
227 			result.cg_image += "/" + path[i];
228 	}
229 
230 	return result;
231 }
232 
profile_spec_equal(parsed_filename const & parsed)233 bool parsed_filename::profile_spec_equal(parsed_filename const & parsed)
234 {
235 	return 	event == parsed.event &&
236 		count == parsed.count &&
237 		unitmask == parsed.unitmask &&
238 		tgid == parsed.tgid &&
239 		tid == parsed.tid &&
240 		cpu == parsed.tid;
241 }
242 
operator <<(ostream & out,parsed_filename const & data)243 ostream & operator<<(ostream & out, parsed_filename const & data)
244 {
245 	out << data.filename << endl;
246 	out << data.image << " " << data.lib_image << " "
247 	    << data.event << " " << data.count << " "
248 	    << data.unitmask << " " << data.tgid << " "
249 	    << data.tid << " " << data.cpu << endl;
250 
251 	return out;
252 }
253