]>
Commit | Line | Data |
---|---|---|
1b78aef5 | 1 | // Copyright (C) Andrew Tridgell 2002 (original file) |
b58abde1 | 2 | // Copyright (C) 2006-2014 Red Hat Inc. (systemtap changes) |
dff50e09 | 3 | // |
1b78aef5 DS |
4 | // This program is free software; you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation; either version 2 of the License, or | |
7 | // (at your option) any later version. | |
dff50e09 | 8 | // |
1b78aef5 DS |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
dff50e09 | 13 | // |
1b78aef5 | 14 | // You should have received a copy of the GNU General Public License |
e8daaf60 | 15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
1b78aef5 DS |
16 | |
17 | #include "config.h" | |
18 | #include "session.h" | |
19 | #include "hash.h" | |
20 | #include "util.h" | |
3b579393 FCE |
21 | |
22 | #include <cstdlib> | |
dd936721 JS |
23 | #include <cstring> |
24 | #include <fstream> | |
1b78aef5 DS |
25 | #include <sstream> |
26 | #include <iomanip> | |
27 | #include <cerrno> | |
28 | ||
29 | extern "C" { | |
30 | #include <sys/types.h> | |
31 | #include <sys/stat.h> | |
32 | #include <unistd.h> | |
dd936721 | 33 | #include "mdfour.h" |
1b78aef5 DS |
34 | } |
35 | ||
36 | using namespace std; | |
37 | ||
dd936721 | 38 | |
dec33462 | 39 | class stap_hash |
dd936721 JS |
40 | { |
41 | private: | |
42 | struct mdfour md4; | |
43 | std::ostringstream parm_stream; | |
44 | ||
45 | public: | |
dec33462 FCE |
46 | stap_hash() { start(); } |
47 | stap_hash(const stap_hash &base) { md4 = base.md4; parm_stream << base.parm_stream.str(); } | |
dd936721 JS |
48 | |
49 | void start(); | |
50 | ||
333040ca CM |
51 | void add(const std::string& description, const unsigned char *buffer, size_t size); |
52 | template<typename T> void add(const std::string& d, const T& x); | |
53 | void add(const std::string& d, const char *s) { add((const std::string&)d, (const unsigned char *)s, strlen(s)); } | |
54 | void add(const std:: string& d, const std::string& s) { add(d, (const unsigned char *)s.c_str(), s.length()); } | |
9beca2c0 | 55 | |
333040ca | 56 | void add_path(const std::string& description, const std::string& path); |
dd936721 JS |
57 | |
58 | void result(std::string& r); | |
178b5ebc | 59 | std::string get_parms() { return parm_stream.str(); } |
dd936721 JS |
60 | }; |
61 | ||
62 | ||
1b78aef5 | 63 | void |
dec33462 | 64 | stap_hash::start() |
1b78aef5 DS |
65 | { |
66 | mdfour_begin(&md4); | |
67 | } | |
68 | ||
69 | ||
70 | void | |
dec33462 | 71 | stap_hash::add(const std::string& description, const unsigned char *buffer, size_t size) |
1b78aef5 | 72 | { |
affcbf48 | 73 | parm_stream << description << buffer << endl; |
1b78aef5 DS |
74 | mdfour_update(&md4, buffer, size); |
75 | } | |
76 | ||
77 | ||
9beca2c0 | 78 | template <typename T> void |
dec33462 | 79 | stap_hash::add(const std::string& d, const T& x) |
9beca2c0 | 80 | { |
333040ca | 81 | parm_stream << d << x << endl; |
9beca2c0 JS |
82 | mdfour_update(&md4, (const unsigned char *)&x, sizeof(x)); |
83 | } | |
84 | ||
85 | ||
2a8c27f6 | 86 | void |
dec33462 | 87 | stap_hash::add_path(const std::string& description, const std::string& path) |
2a8c27f6 JS |
88 | { |
89 | struct stat st; | |
e7899657 | 90 | memset (&st, 0, sizeof(st)); |
2a8c27f6 | 91 | |
413c55ed | 92 | if (stat(path.c_str(), &st) != 0) |
9a193b06 JS |
93 | st.st_size = st.st_mtime = -1; |
94 | ||
333040ca CM |
95 | add(description + "Path: ", path); |
96 | add(description + "Size: ", st.st_size); | |
97 | add(description + "Timestamp: ", st.st_mtime); | |
2a8c27f6 JS |
98 | } |
99 | ||
100 | ||
1b78aef5 | 101 | void |
dec33462 | 102 | stap_hash::result(string& r) |
1b78aef5 DS |
103 | { |
104 | ostringstream rstream; | |
105 | unsigned char sum[16]; | |
106 | ||
107 | mdfour_update(&md4, NULL, 0); | |
108 | mdfour_result(&md4, sum); | |
109 | ||
110 | for (int i=0; i<16; i++) | |
111 | { | |
112 | rstream << hex << setfill('0') << setw(2) << (unsigned)sum[i]; | |
113 | } | |
114 | rstream << "_" << setw(0) << dec << (unsigned)md4.totalN; | |
115 | r = rstream.str(); | |
116 | } | |
117 | ||
0d1ad607 WH |
118 | void create_hash_log(const string &type_str, const string &parms, const string &result, const string &hash_log_path) |
119 | { | |
120 | ofstream log_file; | |
121 | time_t rawtime; | |
122 | time ( &rawtime ); | |
123 | string time_str(ctime (&rawtime)); | |
124 | ||
125 | log_file.open(hash_log_path.c_str()); | |
126 | log_file << "[" << time_str.substr(0,time_str.length()-1); // erase terminated '\n' | |
178b5ebc JS |
127 | log_file << "] " << type_str << ":" << endl; |
128 | log_file << parms << endl; | |
659d42ce | 129 | log_file << _("result:") << result << endl; |
0d1ad607 WH |
130 | log_file.close(); |
131 | } | |
1b78aef5 | 132 | |
dec33462 | 133 | static const stap_hash& |
9b3c54b2 | 134 | get_base_hash (systemtap_session& s) |
1b78aef5 | 135 | { |
9b3c54b2 JS |
136 | if (s.base_hash) |
137 | return *s.base_hash; | |
138 | ||
dec33462 FCE |
139 | s.base_hash = new stap_hash(); |
140 | stap_hash& h = *s.base_hash; | |
9b3c54b2 | 141 | |
b58abde1 FCE |
142 | // Hash systemtap version |
143 | h.add("Systemtap version: ", s.version_string()); | |
144 | ||
5c54d49e | 145 | // Hash kernel release and arch. |
333040ca CM |
146 | h.add("Kernel Release: ", s.kernel_release); |
147 | h.add_path("Kernel Build Tree ", s.kernel_build_tree); | |
148 | h.add("Architecture: ", s.architecture); | |
5c54d49e | 149 | |
2a8c27f6 JS |
150 | // Hash a few kernel version/build-id files too |
151 | // (useful for kernel developers reusing a single source tree) | |
333040ca CM |
152 | h.add_path("Kernel Build Tree .config ", s.kernel_build_tree + "/.config"); |
153 | h.add_path("Kernel Build Tree .version ", s.kernel_build_tree + "/.version"); | |
154 | h.add_path("Kernel Build Tree compile.h ", s.kernel_build_tree + "/include/linux/compile.h"); | |
155 | h.add_path("Kernel Build Tree version.h ", s.kernel_build_tree + "/include/linux/version.h"); | |
156 | h.add_path("Kernel Build Tree utsrelease.h ", s.kernel_build_tree + "/include/linux/utsrelease.h"); | |
2a8c27f6 | 157 | |
d89da127 SM |
158 | // Also hash guru mode flag, since behaviour can diverge. |
159 | h.add("Guru mode ", s.guru_mode); | |
160 | ||
5c54d49e | 161 | // Hash runtime path (that gets added in as "-R path"). |
333040ca | 162 | h.add_path("Runtime ", s.runtime_path); |
e057551e JS |
163 | h.add_path("Runtime transport ", s.runtime_path + "/transport"); |
164 | h.add_path("Runtime unwind ", s.runtime_path + "/unwind"); | |
165 | h.add_path("Runtime sub ", s.runtime_path + | |
ac3af990 | 166 | (s.runtime_usermode_p() ? "/dyninst" : "/linux")); |
5c54d49e JS |
167 | |
168 | // Hash compiler path, size, and mtime. We're just going to assume | |
169 | // we'll be using gcc. XXX: getting kbuild to spit out out would be | |
413c55ed | 170 | // better, especially since this is fooled by ccache. |
0a567f6d | 171 | h.add_path("Compiler ", find_executable("gcc")); |
5c54d49e JS |
172 | |
173 | // Hash the systemtap size and mtime. We could use VERSION/DATE, | |
174 | // but when developing systemtap that doesn't work well (since you | |
175 | // can compile systemtap multiple times in 1 day). Since we don't | |
176 | // know exactly where we're getting run from, we'll use | |
28946fe7 MW |
177 | // /proc/self/exe (and we resolve it ourselves to help valgrind). |
178 | h.add_path("Systemtap ", get_self_path()); | |
9b3c54b2 JS |
179 | |
180 | return h; | |
5c54d49e JS |
181 | } |
182 | ||
183 | ||
184 | static bool | |
185 | create_hashdir (systemtap_session& s, const string& result, string& hashdir) | |
186 | { | |
187 | int nlevels = 1; | |
188 | ||
189 | // Use a N level subdir for the cache path to reduce the impact on | |
190 | // filesystems which are slow for large directories. Let N be adjustable. | |
191 | const char *s_n = getenv("SYSTEMTAP_NLEVELS"); | |
192 | if (s_n) | |
1b78aef5 DS |
193 | { |
194 | nlevels = atoi(s_n); | |
195 | if (nlevels < 1) nlevels = 1; | |
196 | if (nlevels > 8) nlevels = 8; | |
197 | } | |
198 | ||
5c54d49e JS |
199 | hashdir = s.cache_path; |
200 | ||
201 | for (int i = 0; i < nlevels; i++) | |
202 | { | |
203 | hashdir += string("/") + result[i*2] + result[i*2 + 1]; | |
204 | if (create_dir(hashdir.c_str()) != 0) | |
205 | { | |
2713ea24 | 206 | s.print_warning("failed to create cache directory (\"" + hashdir + "\") " + strerror(errno) + ", disabling cache support"); |
63d530ab | 207 | s.use_cache = s.use_script_cache = false; |
5c54d49e JS |
208 | return false; |
209 | } | |
210 | } | |
211 | return true; | |
212 | } | |
213 | ||
214 | ||
9b3c54b2 JS |
215 | void |
216 | find_script_hash (systemtap_session& s, const string& script) | |
5c54d49e | 217 | { |
dec33462 | 218 | stap_hash h(get_base_hash(s)); |
5c54d49e | 219 | |
1b78aef5 DS |
220 | // Hash getuid. This really shouldn't be necessary (since who you |
221 | // are doesn't change the generated output), but the hash gets used | |
222 | // as the module name. If two different users try to run the same | |
223 | // script at the same time, we need something to differentiate the | |
224 | // module name. | |
333040ca | 225 | h.add("UID: ", getuid()); |
1b78aef5 | 226 | |
1b78aef5 | 227 | // Hash user-specified arguments (that change the generated module). |
7baf48e9 FCE |
228 | h.add("Bulk Mode (-b): ", s.bulk_mode); |
229 | h.add("Timing (-t): ", s.timing); | |
7baf48e9 FCE |
230 | h.add("Skip Badvars (--skip-badvars): ", s.skip_badvars); |
231 | h.add("Privilege (--privilege): ", s.privilege); | |
232 | h.add("Compatible (--compatible): ", s.compatible); | |
7baf48e9 | 233 | h.add("Error suppression (--suppress-handler-errors): ", s.suppress_handler_errors); |
152fa051 | 234 | h.add("Suppress Time Limits (--suppress-time-limits): ", s.suppress_time_limits); |
30833b32 MC |
235 | h.add("Prologue Searching (--prologue-searching[=WHEN]): ", int(s.prologue_searching_mode)); |
236 | ||
08fce398 SM |
237 | for (unsigned i = 0; i < s.c_macros.size(); i++) |
238 | h.add("Macros: ", s.c_macros[i]); | |
1b78aef5 | 239 | |
4569f6bb | 240 | // Add any custom kbuild flags (-B) |
1392896d | 241 | for (unsigned i = 0; i < s.kbuildflags.size(); i++) |
333040ca | 242 | h.add("Kbuildflags: ", s.kbuildflags[i]); |
1392896d | 243 | |
633e5ca7 FCE |
244 | // Add any custom --modinfo strings |
245 | for (unsigned i = 0; i < s.modinfos.size(); i++) | |
246 | h.add("MODULE_INFO: ", s.modinfos[i]); | |
247 | ||
a7f52597 FCE |
248 | // -d MODULE |
249 | for (set<string>::iterator it = s.unwindsym_modules.begin(); | |
250 | it != s.unwindsym_modules.end(); | |
251 | it++) | |
333040ca | 252 | h.add_path("Unwindsym Modules ", *it); |
ecd129af CM |
253 | |
254 | // Add the build id of each module | |
255 | for(vector<string>::iterator it = s.build_ids.begin(); | |
256 | it != s.build_ids.end(); | |
257 | it++) | |
333040ca | 258 | h.add("Build ID: ", *it); |
a7f52597 | 259 | |
1b78aef5 | 260 | // Add in pass 2 script output. |
affcbf48 | 261 | h.add("Script:\n", script); |
1b78aef5 | 262 | |
5c54d49e JS |
263 | // Get the directory path to store our cached script |
264 | string result, hashdir; | |
1b78aef5 | 265 | h.result(result); |
5c54d49e JS |
266 | if (!create_hashdir(s, result, hashdir)) |
267 | return; | |
1b78aef5 DS |
268 | |
269 | // Update module name to be 'stap_{hash start}'. '{hash start}' | |
270 | // must not be too long. This shouldn't happen, since the maximum | |
271 | // size of a hash is 32 fixed chars + 1 (for the '_') + a max of 11. | |
272 | s.module_name = "stap_" + result; | |
273 | if (s.module_name.size() >= (MODULE_NAME_LEN - 1)) | |
274 | s.module_name.resize(MODULE_NAME_LEN - 1); | |
275 | ||
276 | // 'ccache' would use a hash path of something like: | |
277 | // s.hash_path = hashdir + "/" + result.substr(nlevels); | |
278 | // which would look like: | |
279 | // ~/.stap_cache/A/B/CDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX | |
280 | // | |
281 | // We're using the following so that the module can be used straight | |
282 | // from the cache if desired. This ends up looking like this: | |
283 | // ~/.stap_cache/A/B/stap_ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX.ko | |
4441e344 | 284 | s.hash_path = hashdir + "/" + s.module_filename(); |
1b78aef5 DS |
285 | |
286 | // Update C source name with new module_name. | |
a4b9c3b3 | 287 | s.translated_source = string(s.tmpdir) + "/" + s.module_name + "_src.c"; |
0d1ad607 WH |
288 | create_hash_log(string("script_hash"), h.get_parms(), result, |
289 | hashdir + "/" + s.module_name + "_hash.log"); | |
1b78aef5 | 290 | } |
73267b89 | 291 | |
5c54d49e | 292 | |
9b3c54b2 JS |
293 | void |
294 | find_stapconf_hash (systemtap_session& s) | |
5c54d49e | 295 | { |
dec33462 | 296 | stap_hash h(get_base_hash(s)); |
5c54d49e | 297 | |
4569f6bb JS |
298 | // Add any custom kbuild flags |
299 | for (unsigned i = 0; i < s.kbuildflags.size(); i++) | |
333040ca | 300 | h.add("Kbuildflags: ", s.kbuildflags[i]); |
5c54d49e JS |
301 | |
302 | // Get the directory path to store our cached stapconf parameters | |
303 | string result, hashdir; | |
304 | h.result(result); | |
305 | if (!create_hashdir(s, result, hashdir)) | |
306 | return; | |
307 | ||
de0db58a JS |
308 | s.stapconf_name = "stapconf_" + result + ".h"; |
309 | s.stapconf_path = hashdir + "/" + s.stapconf_name; | |
0d1ad607 WH |
310 | create_hash_log(string("stapconf_hash"), h.get_parms(), result, |
311 | hashdir + "/stapconf_" + result + "_hash.log"); | |
5c54d49e JS |
312 | } |
313 | ||
314 | ||
a2639cb7 | 315 | string |
c9ccb642 | 316 | find_tracequery_hash (systemtap_session& s, const string& header) |
b278033a | 317 | { |
dec33462 | 318 | stap_hash h(get_base_hash(s)); |
b278033a | 319 | |
c9ccb642 FCE |
320 | // Add the tracepoint header to the computed hash |
321 | h.add_path("Header ", header); | |
b278033a | 322 | |
4569f6bb JS |
323 | // Add any custom kbuild flags |
324 | for (unsigned i = 0; i < s.kbuildflags.size(); i++) | |
333040ca | 325 | h.add("Kbuildflags: ", s.kbuildflags[i]); |
4569f6bb | 326 | |
b278033a JS |
327 | // Get the directory path to store our cached module |
328 | string result, hashdir; | |
329 | h.result(result); | |
330 | if (!create_hashdir(s, result, hashdir)) | |
c9ccb642 | 331 | return ""; // XXX: as opposed to throwing an exception? |
b278033a | 332 | |
0d1ad607 WH |
333 | create_hash_log(string("tracequery_hash"), h.get_parms(), result, |
334 | hashdir + "/tracequery_" + result + "_hash.log"); | |
2a0e62a8 | 335 | return hashdir + "/tracequery_" + result + ".o"; |
b278033a JS |
336 | } |
337 | ||
1f329b5e | 338 | |
a2639cb7 JS |
339 | string |
340 | find_typequery_hash (systemtap_session& s, const string& name) | |
1f329b5e | 341 | { |
dec33462 | 342 | stap_hash h(get_base_hash(s)); |
1f329b5e JS |
343 | |
344 | // Add the typequery name to distinguish the hash | |
333040ca | 345 | h.add("Typequery Name: ", name); |
1f329b5e | 346 | |
4569f6bb JS |
347 | if (name[0] == 'k') |
348 | // Add any custom kbuild flags | |
349 | for (unsigned i = 0; i < s.kbuildflags.size(); i++) | |
333040ca | 350 | h.add("Kbuildflags: ", s.kbuildflags[i]); |
4569f6bb | 351 | |
1f329b5e JS |
352 | // Get the directory path to store our cached module |
353 | string result, hashdir; | |
354 | h.result(result); | |
355 | if (!create_hashdir(s, result, hashdir)) | |
a2639cb7 | 356 | return ""; |
1f329b5e | 357 | |
0d1ad607 WH |
358 | create_hash_log(string("typequery_hash"), h.get_parms(), result, |
359 | hashdir + "/typequery_" + result + "_hash.log"); | |
a2639cb7 | 360 | return hashdir + "/typequery_" + result |
1f329b5e JS |
361 | + (name[0] == 'k' ? ".ko" : ".so"); |
362 | } | |
363 | ||
7d26ee02 JS |
364 | |
365 | string | |
366 | find_uprobes_hash (systemtap_session& s) | |
367 | { | |
dec33462 | 368 | stap_hash h(get_base_hash(s)); |
7d26ee02 JS |
369 | |
370 | // Hash runtime uprobes paths | |
e057551e JS |
371 | h.add_path("Uprobes Runtime Path /uprobes ", s.runtime_path + "/linux/uprobes"); |
372 | h.add_path("Uprobes Runtime Path /uprobes2 ", s.runtime_path + "/linux/uprobes2"); | |
7d26ee02 JS |
373 | |
374 | // Add any custom kbuild flags | |
375 | for (unsigned i = 0; i < s.kbuildflags.size(); i++) | |
333040ca | 376 | h.add("Kbuildflags: ", s.kbuildflags[i]); |
7d26ee02 | 377 | |
f63e9688 JS |
378 | // Add any custom --modinfo strings |
379 | for (unsigned i = 0; i < s.modinfos.size(); i++) | |
380 | h.add("MODULE_INFO: ", s.modinfos[i]); | |
381 | ||
7d26ee02 JS |
382 | // Get the directory path to store our cached module |
383 | string result, hashdir; | |
384 | h.result(result); | |
385 | if (!create_hashdir(s, result, hashdir)) | |
386 | return ""; | |
387 | ||
388 | create_hash_log(string("uprobes_hash"), h.get_parms(), result, | |
389 | hashdir + "/uprobes_" + result + "_hash.log"); | |
390 | return hashdir + "/uprobes_" + result; | |
391 | } | |
392 | ||
73267b89 | 393 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |