]> sourceware.org Git - systemtap.git/blob - hash.cxx
Consolidate task_finder/vma tracker initialization.
[systemtap.git] / hash.cxx
1 // Copyright (C) Andrew Tridgell 2002 (original file)
2 // Copyright (C) 2006-2008 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, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 #include "config.h"
19 #include "session.h"
20 #include "hash.h"
21 #include "util.h"
22
23 #include <cstdlib>
24 #include <sstream>
25 #include <iomanip>
26 #include <cerrno>
27
28 extern "C" {
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 }
33
34 using namespace std;
35
36 void
37 hash::start()
38 {
39 mdfour_begin(&md4);
40 }
41
42
43 void
44 hash::add(const unsigned char *buffer, size_t size)
45 {
46 parm_stream << "," << buffer;
47 mdfour_update(&md4, buffer, size);
48 }
49
50
51 void
52 hash::add_file(const std::string& filename)
53 {
54 struct stat st;
55
56 if (stat(filename.c_str(), &st) != 0)
57 st.st_size = st.st_mtime = -1;
58
59 add(filename);
60 add(st.st_size);
61 add(st.st_mtime);
62 }
63
64 string
65 hash::get_parms()
66 {
67 string parms_str = parm_stream.str();
68
69 parm_stream.clear();
70 if (!parms_str.empty())
71 parms_str.erase(parms_str.begin()); // skip leading ","
72 return parms_str;
73 }
74
75 void
76 hash::result(string& r)
77 {
78 ostringstream rstream;
79 unsigned char sum[16];
80
81 mdfour_update(&md4, NULL, 0);
82 mdfour_result(&md4, sum);
83
84 for (int i=0; i<16; i++)
85 {
86 rstream << hex << setfill('0') << setw(2) << (unsigned)sum[i];
87 }
88 rstream << "_" << setw(0) << dec << (unsigned)md4.totalN;
89 r = rstream.str();
90 }
91
92 void create_hash_log(const string &type_str, const string &parms, const string &result, const string &hash_log_path)
93 {
94 ofstream log_file;
95 time_t rawtime;
96 time ( &rawtime );
97 string time_str(ctime (&rawtime));
98
99 log_file.open(hash_log_path.c_str());
100 log_file << "[" << time_str.substr(0,time_str.length()-1); // erase terminated '\n'
101 log_file << "]" << type_str;
102 log_file << ": " << parms << endl;
103 log_file << "result:" << result << endl;
104 log_file.close();
105 }
106
107 static const hash&
108 get_base_hash (systemtap_session& s)
109 {
110 if (s.base_hash)
111 return *s.base_hash;
112
113 s.base_hash = new hash();
114 hash& h = *s.base_hash;
115
116 // Hash kernel release and arch.
117 h.add(s.kernel_release);
118 h.add(s.kernel_build_tree);
119 h.add(s.architecture);
120
121 // Hash a few kernel version/build-id files too
122 // (useful for kernel developers reusing a single source tree)
123 h.add_file(s.kernel_build_tree + "/.config");
124 h.add_file(s.kernel_build_tree + "/.version");
125 h.add_file(s.kernel_build_tree + "/include/linux/compile.h");
126 h.add_file(s.kernel_build_tree + "/include/linux/version.h");
127 h.add_file(s.kernel_build_tree + "/include/linux/utsrelease.h");
128
129 // If the kernel is a git working directory, then add the git HEAD
130 // revision to our hash as well.
131 // XXX avoiding this for now, because it's potentially expensive and has
132 // uncertain gain. The only corner case that this may help is if a developer
133 // is switching the source tree without rebuilding the kernel...
134 ///h.add(git_revision(s.kernel_build_tree));
135
136 // Hash runtime path (that gets added in as "-R path").
137 h.add(s.runtime_path);
138
139 // Hash compiler path, size, and mtime. We're just going to assume
140 // we'll be using gcc. XXX: getting kbuild to spit out out would be
141 // better.
142 h.add_file(find_executable("gcc"));
143
144 // Hash the systemtap size and mtime. We could use VERSION/DATE,
145 // but when developing systemtap that doesn't work well (since you
146 // can compile systemtap multiple times in 1 day). Since we don't
147 // know exactly where we're getting run from, we'll use
148 // /proc/self/exe.
149 h.add_file("/proc/self/exe");
150
151 return h;
152 }
153
154
155 static bool
156 create_hashdir (systemtap_session& s, const string& result, string& hashdir)
157 {
158 int nlevels = 1;
159
160 // Use a N level subdir for the cache path to reduce the impact on
161 // filesystems which are slow for large directories. Let N be adjustable.
162 const char *s_n = getenv("SYSTEMTAP_NLEVELS");
163 if (s_n)
164 {
165 nlevels = atoi(s_n);
166 if (nlevels < 1) nlevels = 1;
167 if (nlevels > 8) nlevels = 8;
168 }
169
170 hashdir = s.cache_path;
171
172 for (int i = 0; i < nlevels; i++)
173 {
174 hashdir += string("/") + result[i*2] + result[i*2 + 1];
175 if (create_dir(hashdir.c_str()) != 0)
176 {
177 if (! s.suppress_warnings)
178 cerr << "Warning: failed to create cache directory (\""
179 << hashdir + "\"): " << strerror(errno)
180 << ", disabling cache support." << endl;
181 s.use_cache = s.use_script_cache = false;
182 return false;
183 }
184 }
185 return true;
186 }
187
188
189 void
190 find_script_hash (systemtap_session& s, const string& script)
191 {
192 hash h(get_base_hash(s));
193 struct stat st;
194
195 // Hash getuid. This really shouldn't be necessary (since who you
196 // are doesn't change the generated output), but the hash gets used
197 // as the module name. If two different users try to run the same
198 // script at the same time, we need something to differentiate the
199 // module name.
200 h.add(getuid());
201
202 // Hash user-specified arguments (that change the generated module).
203 h.add(s.bulk_mode); // '-b'
204 h.add(s.timing); // '-t'
205 h.add(s.prologue_searching); // '-P'
206 h.add(s.ignore_vmlinux); // --ignore-vmlinux
207 h.add(s.ignore_dwarf); // --ignore-dwarf
208 h.add(s.consult_symtab); // --kelf, --kmap
209 h.add(s.skip_badvars); // --skip-badvars
210 h.add(s.unprivileged); // --unprivileged
211 h.add(s.omit_werror); // undocumented, evil
212 if (!s.kernel_symtab_path.empty()) // --kmap
213 {
214 h.add(s.kernel_symtab_path);
215 if (stat(s.kernel_symtab_path.c_str(), &st) == 0)
216 {
217 // NB: stat of /proc/kallsyms always returns size=0, mtime=now...
218 // which is a good reason to use the default /boot/System.map-2.6.xx
219 // instead.
220 h.add(st.st_size);
221 h.add(st.st_mtime);
222 }
223 }
224 for (unsigned i = 0; i < s.macros.size(); i++)
225 h.add(s.macros[i]);
226
227 // Add any custom kbuild flags (-B)
228 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
229 h.add(s.kbuildflags[i]);
230
231 // -d MODULE
232 for (set<string>::iterator it = s.unwindsym_modules.begin();
233 it != s.unwindsym_modules.end();
234 it++)
235 h.add(*it);
236 // XXX: a build-id of each module might be even better
237
238 // Add in pass 2 script output.
239 h.add(script);
240
241 // Get the directory path to store our cached script
242 string result, hashdir;
243 h.result(result);
244 if (!create_hashdir(s, result, hashdir))
245 return;
246
247 // Update module name to be 'stap_{hash start}'. '{hash start}'
248 // must not be too long. This shouldn't happen, since the maximum
249 // size of a hash is 32 fixed chars + 1 (for the '_') + a max of 11.
250 s.module_name = "stap_" + result;
251 if (s.module_name.size() >= (MODULE_NAME_LEN - 1))
252 s.module_name.resize(MODULE_NAME_LEN - 1);
253
254 // 'ccache' would use a hash path of something like:
255 // s.hash_path = hashdir + "/" + result.substr(nlevels);
256 // which would look like:
257 // ~/.stap_cache/A/B/CDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX
258 //
259 // We're using the following so that the module can be used straight
260 // from the cache if desired. This ends up looking like this:
261 // ~/.stap_cache/A/B/stap_ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX.ko
262 s.hash_path = hashdir + "/" + s.module_name + ".ko";
263
264 // Update C source name with new module_name.
265 s.translated_source = string(s.tmpdir) + "/" + s.module_name + ".c";
266 create_hash_log(string("script_hash"), h.get_parms(), result,
267 hashdir + "/" + s.module_name + "_hash.log");
268 }
269
270
271 void
272 find_stapconf_hash (systemtap_session& s)
273 {
274 hash h(get_base_hash(s));
275
276 // Add any custom kbuild flags
277 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
278 h.add(s.kbuildflags[i]);
279
280 // Get the directory path to store our cached stapconf parameters
281 string result, hashdir;
282 h.result(result);
283 if (!create_hashdir(s, result, hashdir))
284 return;
285
286 s.stapconf_name = "stapconf_" + result + ".h";
287 s.stapconf_path = hashdir + "/" + s.stapconf_name;
288 create_hash_log(string("stapconf_hash"), h.get_parms(), result,
289 hashdir + "/stapconf_" + result + "_hash.log");
290 }
291
292
293 string
294 find_tracequery_hash (systemtap_session& s, const vector<string>& headers)
295 {
296 hash h(get_base_hash(s));
297
298 // Add the tracepoint headers to the computed hash
299 for (size_t i = 0; i < headers.size(); ++i)
300 h.add_file(headers[i]);
301
302 // Add any custom kbuild flags
303 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
304 h.add(s.kbuildflags[i]);
305
306 // Get the directory path to store our cached module
307 string result, hashdir;
308 h.result(result);
309 if (!create_hashdir(s, result, hashdir))
310 return "";
311
312 create_hash_log(string("tracequery_hash"), h.get_parms(), result,
313 hashdir + "/tracequery_" + result + "_hash.log");
314 return hashdir + "/tracequery_" + result + ".ko";
315 }
316
317
318 string
319 find_typequery_hash (systemtap_session& s, const string& name)
320 {
321 hash h(get_base_hash(s));
322
323 // Add the typequery name to distinguish the hash
324 h.add(name);
325
326 if (name[0] == 'k')
327 // Add any custom kbuild flags
328 for (unsigned i = 0; i < s.kbuildflags.size(); i++)
329 h.add(s.kbuildflags[i]);
330
331 // Get the directory path to store our cached module
332 string result, hashdir;
333 h.result(result);
334 if (!create_hashdir(s, result, hashdir))
335 return "";
336
337 create_hash_log(string("typequery_hash"), h.get_parms(), result,
338 hashdir + "/typequery_" + result + "_hash.log");
339 return hashdir + "/typequery_" + result
340 + (name[0] == 'k' ? ".ko" : ".so");
341 }
342
343 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.053601 seconds and 5 git commands to generate.