]> sourceware.org Git - systemtap.git/blob - hash.cxx
Only run .plt tests on x86.
[systemtap.git] / hash.cxx
1 // Copyright (C) Andrew Tridgell 2002 (original file)
2 // Copyright (C) 2006-2011 Red Hat Inc. (systemtap changes)
3 //
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.
8 //
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.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 #include "config.h"
18 #include "session.h"
19 #include "hash.h"
20 #include "util.h"
21
22 #include <cstdlib>
23 #include <cstring>
24 #include <fstream>
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>
33 #include "mdfour.h"
34 }
35
36 using namespace std;
37
38
39 class hash
40 {
41 private:
42 struct mdfour md4;
43 std::ostringstream parm_stream;
44
45 public:
46 hash() { start(); }
47 hash(const hash &base) { md4 = base.md4; parm_stream << base.parm_stream.str(); }
48
49 void start();
50
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()); }
55
56 void add_path(const std::string& description, const std::string& path);
57
58 void result(std::string& r);
59 std::string get_parms() { return parm_stream.str(); }
60 };
61
62
63 void
64 hash::start()
65 {
66 mdfour_begin(&md4);
67 }
68
69
70 void
71 hash::add(const std::string& description, const unsigned char *buffer, size_t size)
72 {
73 parm_stream << description << buffer << endl;
74 mdfour_update(&md4, buffer, size);
75 }
76
77
78 template <typename T> void
79 hash::add(const std::string& d, const T& x)
80 {
81 parm_stream << d << x << endl;
82 mdfour_update(&md4, (const unsigned char *)&x, sizeof(x));
83 }
84
85
86 void
87 hash::add_path(const std::string& description, const std::string& path)
88 {
89 struct stat st;
90 memset (&st, 0, sizeof(st));
91
92 if (stat(path.c_str(), &st) != 0)
93 st.st_size = st.st_mtime = -1;
94
95 add(description + "Path: ", path);
96 add(description + "Size: ", st.st_size);
97 add(description + "Timestamp: ", st.st_mtime);
98 }
99
100
101 void
102 hash::result(string& r)
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
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'
127 log_file << "] " << type_str << ":" << endl;
128 log_file << parms << endl;
129 log_file << _("result:") << result << endl;
130 log_file.close();
131 }
132
133 static const hash&
134 get_base_hash (systemtap_session& s)
135 {
136 if (s.base_hash)
137 return *s.base_hash;
138
139 s.base_hash = new hash();
140 hash& h = *s.base_hash;
141
142 // Hash kernel release and arch.
143 h.add("Kernel Release: ", s.kernel_release);
144 h.add_path("Kernel Build Tree ", s.kernel_build_tree);
145 h.add("Architecture: ", s.architecture);
146
147 // Hash a few kernel version/build-id files too
148 // (useful for kernel developers reusing a single source tree)
149 h.add_path("Kernel Build Tree .config ", s.kernel_build_tree + "/.config");
150 h.add_path("Kernel Build Tree .version ", s.kernel_build_tree + "/.version");
151 h.add_path("Kernel Build Tree compile.h ", s.kernel_build_tree + "/include/linux/compile.h");
152 h.add_path("Kernel Build Tree version.h ", s.kernel_build_tree + "/include/linux/version.h");
153 h.add_path("Kernel Build Tree utsrelease.h ", s.kernel_build_tree + "/include/linux/utsrelease.h");
154
155 // Hash runtime path (that gets added in as "-R path").
156 h.add_path("Runtime ", s.runtime_path);
157
158 // Hash compiler path, size, and mtime. We're just going to assume
159 // we'll be using gcc. XXX: getting kbuild to spit out out would be
160 // better, especially since this is fooled by ccache.
161 h.add_path("Compiler ", find_executable("gcc"));
162
163 // Hash the systemtap size and mtime. We could use VERSION/DATE,
164 // but when developing systemtap that doesn't work well (since you
165 // can compile systemtap multiple times in 1 day). Since we don't
166 // know exactly where we're getting run from, we'll use
167 // /proc/self/exe.
168 // XXX well almost exactly -- valgrind throws this off
169 h.add_path("Systemtap ", "/proc/self/exe");
170
171 return h;
172 }
173
174
175 static bool
176 create_hashdir (systemtap_session& s, const string& result, string& hashdir)
177 {
178 int nlevels = 1;
179
180 // Use a N level subdir for the cache path to reduce the impact on
181 // filesystems which are slow for large directories. Let N be adjustable.
182 const char *s_n = getenv("SYSTEMTAP_NLEVELS");
183 if (s_n)
184 {
185 nlevels = atoi(s_n);
186 if (nlevels < 1) nlevels = 1;
187 if (nlevels > 8) nlevels = 8;
188 }
189
190 hashdir = s.cache_path;
191
192 for (int i = 0; i < nlevels; i++)
193 {
194 hashdir += string("/") + result[i*2] + result[i*2 + 1];
195 if (create_dir(hashdir.c_str()) != 0)
196 {
197 s.print_warning("failed to create cache directory (\"" + hashdir + "\") " + strerror(errno) + ", disabling cache support");
198 s.use_cache = s.use_script_cache = false;
199 return false;
200 }
201 }
202 return true;
203 }
204
205
206 void
207 find_script_hash (systemtap_session& s, const string& script)
208 {
209 hash h(get_base_hash(s));
210 struct stat st;
211
212 // Hash getuid. This really shouldn't be necessary (since who you
213 // are doesn't change the generated output), but the hash gets used
214 // as the module name. If two different users try to run the same
215 // script at the same time, we need something to differentiate the
216 // module name.
217 h.add("UID: ", getuid());
218
219 // Hash user-specified arguments (that change the generated module).
220 h.add("Bulk Mode (-b): ", s.bulk_mode);
221 h.add("Timing (-t): ", s.timing);
222 h.add("Prologue Searching (-P): ", s.prologue_searching);
223 h.add("Ignore Vmlinux (--ignore-vmlinux): ", s.ignore_vmlinux);
224 h.add("Ignore Dwarf (--ignore-dwarf): ", s.ignore_dwarf);
225 h.add("Consult Symtab (--kelf, --kmap): ", s.consult_symtab);
226 h.add("Skip Badvars (--skip-badvars): ", s.skip_badvars);
227 h.add("Privilege (--privilege): ", s.privilege);
228 h.add("Compatible (--compatible): ", s.compatible);
229 h.add("Omit Werror (undocumented): ", s.omit_werror);
230 h.add("Prologue Searching (-P): ", s.prologue_searching);
231 h.add("Error suppression (--suppress-handler-errors): ", s.suppress_handler_errors);
232 if (!s.kernel_symtab_path.empty()) // --kmap
233 {
234 h.add("Kernel Symtab Path: ", s.kernel_symtab_path);
235 if (stat(s.kernel_symtab_path.c_str(), &st) == 0)
236 {
237 // NB: stat of /proc/kallsyms always returns size=0, mtime=now...
238 // which is a good reason to use the default /boot/System.map-2.6.xx
239 // instead.
240 h.add("Kernel Symtab Size: ", st.st_size);
241 h.add("Kernel Symtab Timestamp: ", st.st_mtime);
242 }
243 }
244 for (unsigned i = 0; i < s.macros.size(); i++)
245 h.add("Macros: ", s.macros[i]);
246
247 // Add any custom kbuild flags (-B)
248 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
249 h.add("Kbuildflags: ", s.kbuildflags[i]);
250
251 // Add any custom --modinfo strings
252 for (unsigned i = 0; i < s.modinfos.size(); i++)
253 h.add("MODULE_INFO: ", s.modinfos[i]);
254
255 // -d MODULE
256 for (set<string>::iterator it = s.unwindsym_modules.begin();
257 it != s.unwindsym_modules.end();
258 it++)
259 h.add_path("Unwindsym Modules ", *it);
260
261 // Add the build id of each module
262 for(vector<string>::iterator it = s.build_ids.begin();
263 it != s.build_ids.end();
264 it++)
265 h.add("Build ID: ", *it);
266
267 // Add in pass 2 script output.
268 h.add("Script:\n", script);
269
270 // Get the directory path to store our cached script
271 string result, hashdir;
272 h.result(result);
273 if (!create_hashdir(s, result, hashdir))
274 return;
275
276 // Update module name to be 'stap_{hash start}'. '{hash start}'
277 // must not be too long. This shouldn't happen, since the maximum
278 // size of a hash is 32 fixed chars + 1 (for the '_') + a max of 11.
279 s.module_name = "stap_" + result;
280 if (s.module_name.size() >= (MODULE_NAME_LEN - 1))
281 s.module_name.resize(MODULE_NAME_LEN - 1);
282
283 // 'ccache' would use a hash path of something like:
284 // s.hash_path = hashdir + "/" + result.substr(nlevels);
285 // which would look like:
286 // ~/.stap_cache/A/B/CDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX
287 //
288 // We're using the following so that the module can be used straight
289 // from the cache if desired. This ends up looking like this:
290 // ~/.stap_cache/A/B/stap_ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX.ko
291 s.hash_path = hashdir + "/" + s.module_name + ".ko";
292
293 // Update C source name with new module_name.
294 s.translated_source = string(s.tmpdir) + "/" + s.module_name + "_src.c";
295 create_hash_log(string("script_hash"), h.get_parms(), result,
296 hashdir + "/" + s.module_name + "_hash.log");
297 }
298
299
300 void
301 find_stapconf_hash (systemtap_session& s)
302 {
303 hash h(get_base_hash(s));
304
305 // Add any custom kbuild flags
306 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
307 h.add("Kbuildflags: ", s.kbuildflags[i]);
308
309 // Get the directory path to store our cached stapconf parameters
310 string result, hashdir;
311 h.result(result);
312 if (!create_hashdir(s, result, hashdir))
313 return;
314
315 s.stapconf_name = "stapconf_" + result + ".h";
316 s.stapconf_path = hashdir + "/" + s.stapconf_name;
317 create_hash_log(string("stapconf_hash"), h.get_parms(), result,
318 hashdir + "/stapconf_" + result + "_hash.log");
319 }
320
321
322 string
323 find_tracequery_hash (systemtap_session& s, const string& header)
324 {
325 hash h(get_base_hash(s));
326
327 // Add the tracepoint header to the computed hash
328 h.add_path("Header ", header);
329
330 // Add any custom kbuild flags
331 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
332 h.add("Kbuildflags: ", s.kbuildflags[i]);
333
334 // Get the directory path to store our cached module
335 string result, hashdir;
336 h.result(result);
337 if (!create_hashdir(s, result, hashdir))
338 return ""; // XXX: as opposed to throwing an exception?
339
340 create_hash_log(string("tracequery_hash"), h.get_parms(), result,
341 hashdir + "/tracequery_" + result + "_hash.log");
342 return hashdir + "/tracequery_" + result + ".o";
343 }
344
345
346 string
347 find_typequery_hash (systemtap_session& s, const string& name)
348 {
349 hash h(get_base_hash(s));
350
351 // Add the typequery name to distinguish the hash
352 h.add("Typequery Name: ", name);
353
354 if (name[0] == 'k')
355 // Add any custom kbuild flags
356 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
357 h.add("Kbuildflags: ", s.kbuildflags[i]);
358
359 // Get the directory path to store our cached module
360 string result, hashdir;
361 h.result(result);
362 if (!create_hashdir(s, result, hashdir))
363 return "";
364
365 create_hash_log(string("typequery_hash"), h.get_parms(), result,
366 hashdir + "/typequery_" + result + "_hash.log");
367 return hashdir + "/typequery_" + result
368 + (name[0] == 'k' ? ".ko" : ".so");
369 }
370
371
372 string
373 find_uprobes_hash (systemtap_session& s)
374 {
375 hash h(get_base_hash(s));
376
377 // Hash runtime uprobes paths
378 h.add_path("Uprobes Runtime Path /uprobes ", s.runtime_path + "/uprobes");
379 h.add_path("Uprobes Runtime Path /uprobes2 ", s.runtime_path + "/uprobes2");
380
381 // Add any custom kbuild flags
382 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
383 h.add("Kbuildflags: ", s.kbuildflags[i]);
384
385 // Add any custom --modinfo strings
386 for (unsigned i = 0; i < s.modinfos.size(); i++)
387 h.add("MODULE_INFO: ", s.modinfos[i]);
388
389 // Get the directory path to store our cached module
390 string result, hashdir;
391 h.result(result);
392 if (!create_hashdir(s, result, hashdir))
393 return "";
394
395 create_hash_log(string("uprobes_hash"), h.get_parms(), result,
396 hashdir + "/uprobes_" + result + "_hash.log");
397 return hashdir + "/uprobes_" + result;
398 }
399
400 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.056346 seconds and 5 git commands to generate.