1 // Copyright (C) Andrew Tridgell 2002 (original file)
2 // Copyright (C) 2006-2011 Red Hat Inc. (systemtap changes)
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.
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.
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/>.
30 #include <sys/types.h>
43 std::ostringstream parm_stream
;
47 hash(const hash
&base
) { md4
= base
.md4
; parm_stream
<< base
.parm_stream
.str(); }
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()); }
56 void add_path(const std::string
& description
, const std::string
& path
);
58 void result(std::string
& r
);
59 std::string
get_parms() { return parm_stream
.str(); }
71 hash::add(const std::string
& description
, const unsigned char *buffer
, size_t size
)
73 parm_stream
<< description
<< buffer
<< endl
;
74 mdfour_update(&md4
, buffer
, size
);
78 template <typename T
> void
79 hash::add(const std::string
& d
, const T
& x
)
81 parm_stream
<< d
<< x
<< endl
;
82 mdfour_update(&md4
, (const unsigned char *)&x
, sizeof(x
));
87 hash::add_path(const std::string
& description
, const std::string
& path
)
90 memset (&st
, 0, sizeof(st
));
92 if (stat(path
.c_str(), &st
) != 0)
93 st
.st_size
= st
.st_mtime
= -1;
95 add(description
+ "Path: ", path
);
96 add(description
+ "Size: ", st
.st_size
);
97 add(description
+ "Timestamp: ", st
.st_mtime
);
102 hash::result(string
& r
)
104 ostringstream rstream
;
105 unsigned char sum
[16];
107 mdfour_update(&md4
, NULL
, 0);
108 mdfour_result(&md4
, sum
);
110 for (int i
=0; i
<16; i
++)
112 rstream
<< hex
<< setfill('0') << setw(2) << (unsigned)sum
[i
];
114 rstream
<< "_" << setw(0) << dec
<< (unsigned)md4
.totalN
;
118 void create_hash_log(const string
&type_str
, const string
&parms
, const string
&result
, const string
&hash_log_path
)
123 string
time_str(ctime (&rawtime
));
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
;
134 get_base_hash (systemtap_session
& s
)
139 s
.base_hash
= new hash();
140 hash
& h
= *s
.base_hash
;
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
);
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");
155 // Hash runtime path (that gets added in as "-R path").
156 h
.add_path("Runtime ", s
.runtime_path
);
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"));
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
168 // XXX well almost exactly -- valgrind throws this off
169 h
.add_path("Systemtap ", "/proc/self/exe");
176 create_hashdir (systemtap_session
& s
, const string
& result
, string
& hashdir
)
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");
186 if (nlevels
< 1) nlevels
= 1;
187 if (nlevels
> 8) nlevels
= 8;
190 hashdir
= s
.cache_path
;
192 for (int i
= 0; i
< nlevels
; i
++)
194 hashdir
+= string("/") + result
[i
*2] + result
[i
*2 + 1];
195 if (create_dir(hashdir
.c_str()) != 0)
197 s
.print_warning("failed to create cache directory (\"" + hashdir
+ "\") " + strerror(errno
) + ", disabling cache support");
198 s
.use_cache
= s
.use_script_cache
= false;
207 find_script_hash (systemtap_session
& s
, const string
& script
)
209 hash
h(get_base_hash(s
));
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
217 h
.add("UID: ", getuid());
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
234 h
.add("Kernel Symtab Path: ", s
.kernel_symtab_path
);
235 if (stat(s
.kernel_symtab_path
.c_str(), &st
) == 0)
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
240 h
.add("Kernel Symtab Size: ", st
.st_size
);
241 h
.add("Kernel Symtab Timestamp: ", st
.st_mtime
);
244 for (unsigned i
= 0; i
< s
.macros
.size(); i
++)
245 h
.add("Macros: ", s
.macros
[i
]);
247 // Add any custom kbuild flags (-B)
248 for (unsigned i
= 0; i
< s
.kbuildflags
.size(); i
++)
249 h
.add("Kbuildflags: ", s
.kbuildflags
[i
]);
251 // Add any custom --modinfo strings
252 for (unsigned i
= 0; i
< s
.modinfos
.size(); i
++)
253 h
.add("MODULE_INFO: ", s
.modinfos
[i
]);
256 for (set
<string
>::iterator it
= s
.unwindsym_modules
.begin();
257 it
!= s
.unwindsym_modules
.end();
259 h
.add_path("Unwindsym Modules ", *it
);
261 // Add the build id of each module
262 for(vector
<string
>::iterator it
= s
.build_ids
.begin();
263 it
!= s
.build_ids
.end();
265 h
.add("Build ID: ", *it
);
267 // Add in pass 2 script output.
268 h
.add("Script:\n", script
);
270 // Get the directory path to store our cached script
271 string result
, hashdir
;
273 if (!create_hashdir(s
, result
, hashdir
))
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);
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
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";
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");
301 find_stapconf_hash (systemtap_session
& s
)
303 hash
h(get_base_hash(s
));
305 // Add any custom kbuild flags
306 for (unsigned i
= 0; i
< s
.kbuildflags
.size(); i
++)
307 h
.add("Kbuildflags: ", s
.kbuildflags
[i
]);
309 // Get the directory path to store our cached stapconf parameters
310 string result
, hashdir
;
312 if (!create_hashdir(s
, result
, hashdir
))
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");
323 find_tracequery_hash (systemtap_session
& s
, const string
& header
)
325 hash
h(get_base_hash(s
));
327 // Add the tracepoint header to the computed hash
328 h
.add_path("Header ", header
);
330 // Add any custom kbuild flags
331 for (unsigned i
= 0; i
< s
.kbuildflags
.size(); i
++)
332 h
.add("Kbuildflags: ", s
.kbuildflags
[i
]);
334 // Get the directory path to store our cached module
335 string result
, hashdir
;
337 if (!create_hashdir(s
, result
, hashdir
))
338 return ""; // XXX: as opposed to throwing an exception?
340 create_hash_log(string("tracequery_hash"), h
.get_parms(), result
,
341 hashdir
+ "/tracequery_" + result
+ "_hash.log");
342 return hashdir
+ "/tracequery_" + result
+ ".o";
347 find_typequery_hash (systemtap_session
& s
, const string
& name
)
349 hash
h(get_base_hash(s
));
351 // Add the typequery name to distinguish the hash
352 h
.add("Typequery Name: ", name
);
355 // Add any custom kbuild flags
356 for (unsigned i
= 0; i
< s
.kbuildflags
.size(); i
++)
357 h
.add("Kbuildflags: ", s
.kbuildflags
[i
]);
359 // Get the directory path to store our cached module
360 string result
, hashdir
;
362 if (!create_hashdir(s
, result
, hashdir
))
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");
373 find_uprobes_hash (systemtap_session
& s
)
375 hash
h(get_base_hash(s
));
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");
381 // Add any custom kbuild flags
382 for (unsigned i
= 0; i
< s
.kbuildflags
.size(); i
++)
383 h
.add("Kbuildflags: ", s
.kbuildflags
[i
]);
385 // Add any custom --modinfo strings
386 for (unsigned i
= 0; i
< s
.modinfos
.size(); i
++)
387 h
.add("MODULE_INFO: ", s
.modinfos
[i
]);
389 // Get the directory path to store our cached module
390 string result
, hashdir
;
392 if (!create_hashdir(s
, result
, hashdir
))
395 create_hash_log(string("uprobes_hash"), h
.get_parms(), result
,
396 hashdir
+ "/uprobes_" + result
+ "_hash.log");
397 return hashdir
+ "/uprobes_" + result
;
400 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */