diff options
Diffstat (limited to 'hash.cxx')
-rw-r--r-- | hash.cxx | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/hash.cxx b/hash.cxx new file mode 100644 index 00000000..26a8ee6f --- /dev/null +++ b/hash.cxx @@ -0,0 +1,168 @@ +// Copyright (C) Andrew Tridgell 2002 (original file) +// Copyright (C) 2006 Red Hat Inc. (systemtap changes) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "config.h" +#include "session.h" +#include "hash.h" +#include "util.h" +#include <sstream> +#include <iomanip> +#include <cerrno> + +extern "C" { +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +} + +using namespace std; + +void +hash::start() +{ + mdfour_begin(&md4); +} + + +void +hash::add(const unsigned char *buffer, size_t size) +{ + mdfour_update(&md4, buffer, size); +} + + +void +hash::result(string& r) +{ + ostringstream rstream; + unsigned char sum[16]; + + mdfour_update(&md4, NULL, 0); + mdfour_result(&md4, sum); + + for (int i=0; i<16; i++) + { + rstream << hex << setfill('0') << setw(2) << (unsigned)sum[i]; + } + rstream << "_" << setw(0) << dec << (unsigned)md4.totalN; + r = rstream.str(); +} + + +// Grabbed from linux/module.h kernel include. +#define MODULE_NAME_LEN (64 - sizeof(unsigned long)) + +void +find_hash (systemtap_session& s, const string& script) +{ + hash h; + int nlevels = 1; + struct stat st; + + // We use a N level subdir for the cache path. Let N be adjustable. + const char *s_n; + if ((s_n = getenv("SYSTEMTAP_NLEVELS"))) + { + nlevels = atoi(s_n); + if (nlevels < 1) nlevels = 1; + if (nlevels > 8) nlevels = 8; + } + + // Hash getuid. This really shouldn't be necessary (since who you + // are doesn't change the generated output), but the hash gets used + // as the module name. If two different users try to run the same + // script at the same time, we need something to differentiate the + // module name. + h.add(getuid()); + + // Hash kernel release and arch. + h.add(s.kernel_release); + h.add(s.architecture); + + // Hash user-specified arguments (that change the generated module). + for (unsigned i = 0; i < s.macros.size(); i++) + h.add(s.macros[i]); + + // Hash runtime path (that gets added in as "-I path"). + h.add(s.runtime_path); + + // Hash compiler path, size, and mtime. We're just going to assume + // we'll be using gcc, which should be correct most of the time. + string gcc_path; + if (find_executable("gcc", gcc_path)) + { + if (stat(gcc_path.c_str(), &st) == 0) + { + h.add(gcc_path); + h.add(st.st_size); + h.add(st.st_mtime); + } + } + + // Hash the systemtap size and mtime. We could use VERSION/DATE, + // but when developing systemtap that doesn't work well (since you + // can compile systemtap multiple times in 1 day). Since we don't + // know exactly where we're getting run from, we'll use + // /proc/self/exe. + if (stat("/proc/self/exe", &st) == 0) + { + h.add(st.st_size); + h.add(st.st_mtime); + } + + // Add in pass 2 script output. + h.add(script); + + // Use a N level subdir for the cache path to reduce the impact on + // filesystems which are slow for large directories. + string hashdir = s.cache_path; + string result; + h.result(result); + + for (int i = 0; i < nlevels; i++) + { + hashdir += string("/") + result[i*2] + result[i*2 + 1]; + if (create_dir(hashdir.c_str()) != 0) + { + cerr << "Warning: failed to create cache directory (\"" + << hashdir + "\"): " << strerror(errno) << endl; + cerr << "Disabling cache support." << endl; + s.use_cache = false; + return; + } + } + + // Update module name to be 'stap_{hash start}'. '{hash start}' + // must not be too long. This shouldn't happen, since the maximum + // size of a hash is 32 fixed chars + 1 (for the '_') + a max of 11. + s.module_name = "stap_" + result; + if (s.module_name.size() >= (MODULE_NAME_LEN - 1)) + s.module_name.resize(MODULE_NAME_LEN - 1); + + // 'ccache' would use a hash path of something like: + // s.hash_path = hashdir + "/" + result.substr(nlevels); + // which would look like: + // ~/.stap_cache/A/B/CDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX + // + // We're using the following so that the module can be used straight + // from the cache if desired. This ends up looking like this: + // ~/.stap_cache/A/B/stap_ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX.ko + s.hash_path = hashdir + "/" + s.module_name + ".ko"; + + // Update C source name with new module_name. + s.translated_source = string(s.tmpdir) + "/" + s.module_name + ".c"; +} |