1 : // Copyright (C) Andrew Tridgell 2002 (original file)
2 : // Copyright (C) 2006-2007 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 347 : hash::start()
38 : {
39 347 : mdfour_begin(&md4);
40 347 : }
41 :
42 :
43 : void
44 4900 : hash::add(const unsigned char *buffer, size_t size)
45 : {
46 4900 : mdfour_update(&md4, buffer, size);
47 4900 : }
48 :
49 :
50 : void
51 347 : hash::result(string& r)
52 : {
53 347 : ostringstream rstream;
54 : unsigned char sum[16];
55 :
56 347 : mdfour_update(&md4, NULL, 0);
57 347 : mdfour_result(&md4, sum);
58 :
59 5899 : for (int i=0; i<16; i++)
60 : {
61 5552 : rstream << hex << setfill('0') << setw(2) << (unsigned)sum[i];
62 : }
63 347 : rstream << "_" << setw(0) << dec << (unsigned)md4.totalN;
64 347 : r = rstream.str();
65 347 : }
66 :
67 :
68 : void
69 347 : find_hash (systemtap_session& s, const string& script)
70 : {
71 347 : hash h;
72 347 : 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 347 : if ((s_n = getenv("SYSTEMTAP_NLEVELS")))
78 : {
79 0 : nlevels = atoi(s_n);
80 0 : if (nlevels < 1) nlevels = 1;
81 0 : 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 347 : h.add(getuid());
90 :
91 : // Hash kernel release and arch.
92 347 : h.add(s.kernel_release);
93 347 : h.add(s.architecture);
94 :
95 : // Hash user-specified arguments (that change the generated module).
96 347 : h.add(s.bulk_mode); // '-b'
97 347 : h.add(s.merge); // '-M'
98 347 : h.add(s.timing); // '-t'
99 347 : h.add(s.prologue_searching); // '-P'
100 389 : for (unsigned i = 0; i < s.macros.size(); i++)
101 42 : h.add(s.macros[i]);
102 :
103 : // Hash runtime path (that gets added in as "-I path").
104 347 : h.add(s.runtime_path);
105 :
106 : // Hash compiler path, size, and mtime. We're just going to assume
107 : // we'll be using gcc, which should be correct most of the time.
108 347 : string gcc_path;
109 347 : if (find_executable("gcc", gcc_path))
110 : {
111 347 : if (stat(gcc_path.c_str(), &st) == 0)
112 : {
113 347 : h.add(gcc_path);
114 347 : h.add(st.st_size);
115 347 : h.add(st.st_mtime);
116 : }
117 : }
118 :
119 : // Hash the systemtap size and mtime. We could use VERSION/DATE,
120 : // but when developing systemtap that doesn't work well (since you
121 : // can compile systemtap multiple times in 1 day). Since we don't
122 : // know exactly where we're getting run from, we'll use
123 : // /proc/self/exe.
124 347 : if (stat("/proc/self/exe", &st) == 0)
125 : {
126 347 : h.add(st.st_size);
127 347 : h.add(st.st_mtime);
128 : }
129 :
130 : // Add in pass 2 script output.
131 347 : h.add(script);
132 :
133 : // Use a N level subdir for the cache path to reduce the impact on
134 : // filesystems which are slow for large directories.
135 347 : string hashdir = s.cache_path;
136 347 : string result;
137 347 : h.result(result);
138 :
139 694 : for (int i = 0; i < nlevels; i++)
140 : {
141 347 : hashdir += string("/") + result[i*2] + result[i*2 + 1];
142 694 : if (create_dir(hashdir.c_str()) != 0)
143 : {
144 : cerr << "Warning: failed to create cache directory (\""
145 0 : << hashdir + "\"): " << strerror(errno) << endl;
146 0 : cerr << "Disabling cache support." << endl;
147 0 : s.use_cache = false;
148 0 : return;
149 : }
150 : }
151 :
152 : // Update module name to be 'stap_{hash start}'. '{hash start}'
153 : // must not be too long. This shouldn't happen, since the maximum
154 : // size of a hash is 32 fixed chars + 1 (for the '_') + a max of 11.
155 347 : s.module_name = "stap_" + result;
156 347 : if (s.module_name.size() >= (MODULE_NAME_LEN - 1))
157 0 : s.module_name.resize(MODULE_NAME_LEN - 1);
158 :
159 : // 'ccache' would use a hash path of something like:
160 : // s.hash_path = hashdir + "/" + result.substr(nlevels);
161 : // which would look like:
162 : // ~/.stap_cache/A/B/CDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX
163 : //
164 : // We're using the following so that the module can be used straight
165 : // from the cache if desired. This ends up looking like this:
166 : // ~/.stap_cache/A/B/stap_ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF_XXX.ko
167 347 : s.hash_path = hashdir + "/" + s.module_name + ".ko";
168 :
169 : // Update C source name with new module_name.
170 347 : s.translated_source = string(s.tmpdir) + "/" + s.module_name + ".c";
171 2188 : }
|