]> sourceware.org Git - systemtap.git/blob - hash.cxx
merged
[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 mdfour_update(&md4, buffer, size);
47 }
48
49
50 void
51 hash::result(string& r)
52 {
53 ostringstream rstream;
54 unsigned char sum[16];
55
56 mdfour_update(&md4, NULL, 0);
57 mdfour_result(&md4, sum);
58
59 for (int i=0; i<16; i++)
60 {
61 rstream << hex << setfill('0') << setw(2) << (unsigned)sum[i];
62 }
63 rstream << "_" << setw(0) << dec << (unsigned)md4.totalN;
64 r = rstream.str();
65 }
66
67
68 void
69 find_hash (systemtap_session& s, const string& script)
70 {
71 hash h;
72 int nlevels = 1;
73 struct stat st;
74
75 // We use a N level subdir for the cache path. Let N be adjustable.
76 const char *s_n;
77 if ((s_n = getenv("SYSTEMTAP_NLEVELS")))
78 {
79 nlevels = atoi(s_n);
80 if (nlevels < 1) nlevels = 1;
81 if (nlevels > 8) nlevels = 8;
82 }
83
84 // Hash getuid. This really shouldn't be necessary (since who you
85 // are doesn't change the generated output), but the hash gets used
86 // as the module name. If two different users try to run the same
87 // script at the same time, we need something to differentiate the
88 // module name.
89 h.add(getuid());
90
91 // Hash kernel release and arch.
92 h.add(s.kernel_release);
93 h.add(s.kernel_build_tree);
94 h.add(s.architecture);
95
96 // Hash user-specified arguments (that change the generated module).
97 h.add(s.bulk_mode); // '-b'
98 h.add(s.merge); // '-M'
99 h.add(s.timing); // '-t'
100 h.add(s.prologue_searching); // '-P'
101 h.add(s.ignore_vmlinux); // --ignore-vmlinux
102 h.add(s.ignore_dwarf); // --ignore-dwarf
103 h.add(s.consult_symtab); // --kelf, --kmap
104 if (!s.kernel_symtab_path.empty()) // --kmap
105 {
106 h.add(s.kernel_symtab_path);
107 if (stat(s.kernel_symtab_path.c_str(), &st) == 0)
108 {
109 // NB: stat of /proc/kallsyms always returns size=0, mtime=now...
110 // which is a good reason to use the default /boot/System.map-2.6.xx
111 // instead.
112 h.add(st.st_size);
113 h.add(st.st_mtime);
114 }
115 }
116 for (unsigned i = 0; i < s.macros.size(); i++)
117 h.add(s.macros[i]);
118
119 // -d MODULE
120 for (set<string>::iterator it = s.unwindsym_modules.begin();
121 it != s.unwindsym_modules.end();
122 it++)
123 h.add(*it);
124 // XXX: a build-id of each module might be even better
125
126 // Hash runtime path (that gets added in as "-R path").
127 h.add(s.runtime_path);
128
129 // Hash compiler path, size, and mtime. We're just going to assume
130 // we'll be using gcc. XXX: getting kbuild to spit out out would be
131 // better.
132 string gcc_path = find_executable ("gcc");
133 if (stat(gcc_path.c_str(), &st) == 0)
134 {
135 h.add(gcc_path);
136 h.add(st.st_size);
137 h.add(st.st_mtime);
138 }
139
140 // Hash the systemtap size and mtime. We could use VERSION/DATE,
141 // but when developing systemtap that doesn't work well (since you
142 // can compile systemtap multiple times in 1 day). Since we don't
143 // know exactly where we're getting run from, we'll use
144 // /proc/self/exe.
145 if (stat("/proc/self/exe", &st) == 0)
146 {
147 h.add(st.st_size);
148 h.add(st.st_mtime);
149 }
150
151 // Add in pass 2 script output.
152 h.add(script);
153
154 // Use a N level subdir for the cache path to reduce the impact on
155 // filesystems which are slow for large directories.
156 string hashdir = s.cache_path;
157 string result;
158 h.result(result);
159
160 for (int i = 0; i < nlevels; i++)
161 {
162 hashdir += string("/") + result[i*2] + result[i*2 + 1];
163 if (create_dir(hashdir.c_str()) != 0)
164 {
165 if (! s.suppress_warnings)
166 cerr << "Warning: failed to create cache directory (\""
167 << hashdir + "\"): " << strerror(errno)
168 << ", disabling cache support." << endl;
169 s.use_cache = false;
170 return;
171 }
172 }
173
174 // Update module name to be 'stap_{hash start}'. '{hash start}'
175 // must not be too long. This shouldn't happen, since the maximum
176 // size of a hash is 32 fixed chars + 1 (for the '_') + a max of 11.
177 s.module_name = "stap_" + result;
178 if (s.module_name.size() >= (MODULE_NAME_LEN - 1))
179 s.module_name.resize(MODULE_NAME_LEN - 1);
180
181 // 'ccache' would use a hash path of something like:
182 // s.hash_path = hashdir + "/" + result.substr(nlevels);
183 // which would look like:
184 // ~/.stap_cache/A/B/CDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX
185 //
186 // We're using the following so that the module can be used straight
187 // from the cache if desired. This ends up looking like this:
188 // ~/.stap_cache/A/B/stap_ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX.ko
189 s.hash_path = hashdir + "/" + s.module_name + ".ko";
190
191 // Update C source name with new module_name.
192 s.translated_source = string(s.tmpdir) + "/" + s.module_name + ".c";
193 }
This page took 0.048341 seconds and 6 git commands to generate.