Branch data Line data Source code
1 : : // systemtap cache manager
2 : : // Copyright (C) 2006-2009 Red Hat Inc.
3 : : //
4 : : // This file is part of systemtap, and is free software. You can
5 : : // redistribute it and/or modify it under the terms of the GNU General
6 : : // Public License (GPL); either version 2, or (at your option) any
7 : : // later version.
8 : :
9 : : #include "config.h"
10 : : #include "session.h"
11 : : #include "cache.h"
12 : : #include "util.h"
13 : : #include "stap-probe.h"
14 : : #include <cerrno>
15 : : #include <string>
16 : : #include <fstream>
17 : : #include <cstring>
18 : : #include <cassert>
19 : : #include <sstream>
20 : : #include <vector>
21 : :
22 : : extern "C" {
23 : : #include <sys/types.h>
24 : : #include <sys/stat.h>
25 : : #include <fcntl.h>
26 : : #include <glob.h>
27 : : #include <regex.h>
28 : : #include <utime.h>
29 : : #include <sys/time.h>
30 : : #include <unistd.h>
31 : : }
32 : :
33 : : using namespace std;
34 : :
35 : :
36 : : #define SYSTEMTAP_CACHE_MAX_FILENAME "cache_mb_limit"
37 : : #define SYSTEMTAP_CACHE_DEFAULT_MB 256
38 : : #define SYSTEMTAP_CACHE_CLEAN_INTERVAL_FILENAME "cache_clean_interval_s"
39 : : #define SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S 300
40 : :
41 : 41745 : struct cache_ent_info {
42 : : vector<string> paths;
43 : : off_t size; // sum across all paths
44 : : time_t mtime; // newest of all paths
45 : :
46 : : cache_ent_info(const vector<string>& paths);
47 : : bool operator<(const struct cache_ent_info& other) const;
48 : : void unlink() const;
49 : : };
50 : :
51 : :
52 : : void
53 : 607 : add_stapconf_to_cache(systemtap_session& s)
54 : : {
55 : 607 : bool verbose = s.verbose > 1;
56 : :
57 [ + - ][ + - ]: 607 : string stapconf_src_path = s.tmpdir + "/" + s.stapconf_name;
[ + - ]
58 [ + - ]: 607 : if (!copy_file(stapconf_src_path, s.stapconf_path, verbose))
59 : : {
60 : : // NB: this is not so severe as to prevent reuse of the .ko
61 : : // already copied.
62 : : //
63 : : // s.use_script_cache = false;
64 : : // return;
65 [ + - ]: 607 : }
66 : 607 : }
67 : :
68 : :
69 : : void
70 : 592 : add_script_to_cache(systemtap_session& s)
71 : : {
72 : 592 : bool verbose = s.verbose > 1;
73 : :
74 : : // PR10543: clean the cache *before* we try putting something new into it.
75 : : // We don't want to risk having the brand new contents being erased again.
76 [ + - ]: 592 : clean_cache(s);
77 : :
78 [ + - ][ + - ]: 592 : string module_src_path = s.tmpdir + "/" + s.module_filename();
[ + - ][ + - ]
[ + - ]
79 [ + - ][ + - ]: 592 : PROBE2(stap, cache__add__module, module_src_path.c_str(), s.hash_path.c_str());
80 [ + - ][ - + ]: 592 : if (!copy_file(module_src_path, s.hash_path, verbose))
81 : : {
82 : 0 : s.use_script_cache = false;
83 : 592 : return;
84 : : }
85 : : // Copy the signature file, if any. It is not an error if this fails.
86 [ + - ][ + - ]: 592 : if (file_exists (module_src_path + ".sgn"))
[ + - ][ - + ]
87 [ # # ][ # # ]: 0 : copy_file(module_src_path + ".sgn", s.hash_path + ".sgn", verbose);
[ # # ][ # # ]
[ # # ]
88 : :
89 [ + - ]: 592 : string c_dest_path = s.hash_path;
90 [ + - ][ - + ]: 592 : if (endswith(c_dest_path, ".ko") || endswith(c_dest_path, ".so"))
[ # # ][ # # ]
[ + - ]
91 [ + - ][ + - ]: 592 : c_dest_path.resize(c_dest_path.size() - 3);
92 [ + - ]: 592 : c_dest_path += ".c";
93 : :
94 [ + - ][ + - ]: 592 : PROBE2(stap, cache__add__source, s.translated_source.c_str(), c_dest_path.c_str());
95 [ + - ]: 592 : if (!copy_file(s.translated_source, c_dest_path, verbose))
96 : : {
97 : : // NB: this is not so severe as to prevent reuse of the .ko
98 : : // already copied.
99 : : //
100 : : // s.use_script_cache = false;
101 [ + - ][ + - ]: 592 : }
[ + - ]
102 : : }
103 : :
104 : :
105 : : bool
106 : 622 : get_stapconf_from_cache(systemtap_session& s)
107 : : {
108 [ - + ]: 622 : if (s.poison_cache)
109 : 0 : return false;
110 : :
111 [ + - ][ + - ]: 622 : string stapconf_dest_path = s.tmpdir + "/" + s.stapconf_name;
[ + - ]
112 : : int fd_stapconf;
113 : :
114 : : // See if stapconf exists
115 [ + - ][ + - ]: 622 : fd_stapconf = open(s.stapconf_path.c_str(), O_RDONLY);
116 [ + + ]: 622 : if (fd_stapconf == -1)
117 : : {
118 : : // It isn't in cache.
119 : 6 : return false;
120 : : }
121 : :
122 : : // Copy the stapconf header file to the destination
123 [ + - ][ + - ]: 1232 : if (!get_file_size(fd_stapconf) ||
[ - + ][ - + ]
124 [ + - ]: 616 : !copy_file(s.stapconf_path, stapconf_dest_path))
125 : : {
126 [ # # ]: 0 : close(fd_stapconf);
127 : 0 : return false;
128 : : }
129 : :
130 : : // We're done with this file handle.
131 [ + - ]: 616 : close(fd_stapconf);
132 : :
133 [ + + ]: 616 : if (s.verbose > 1)
134 [ + - ][ + - ]: 15 : clog << _("Pass 4: using cached ") << s.stapconf_path << endl;
[ + - ]
135 : :
136 [ + - ]: 622 : return true;
137 : : }
138 : :
139 : :
140 : : bool
141 : 814 : get_script_from_cache(systemtap_session& s)
142 : : {
143 [ - + ]: 814 : if (s.poison_cache)
144 : 0 : return false;
145 : :
146 [ + - ][ + - ]: 814 : string module_dest_path = s.tmpdir + "/" + s.module_filename();
[ + - ][ + - ]
[ + - ]
147 [ + - ]: 814 : string c_src_path = s.hash_path;
148 : : int fd_module, fd_c;
149 : :
150 [ + - ][ - + ]: 814 : if (endswith(c_src_path, ".ko") || endswith(c_src_path, ".so"))
[ # # ][ # # ]
[ + - ]
151 [ + - ][ + - ]: 814 : c_src_path.resize(c_src_path.size() - 3);
152 [ + - ]: 814 : c_src_path += ".c";
153 : :
154 : : // See if module exists
155 [ + - ][ + - ]: 814 : fd_module = open(s.hash_path.c_str(), O_RDONLY);
156 [ + + ]: 814 : if (fd_module == -1)
157 : : {
158 : : // It isn't in cache.
159 : 628 : return false;
160 : : }
161 : :
162 : : // See if C file exists.
163 [ + - ][ + - ]: 186 : fd_c = open(c_src_path.c_str(), O_RDONLY);
164 [ - + ]: 186 : if (fd_c == -1)
165 : : {
166 : : // The module is there, but the C file isn't. Cleanup and
167 : : // return.
168 [ # # ]: 0 : close(fd_module);
169 [ # # ]: 0 : unlink(s.hash_path.c_str());
170 : 0 : return false;
171 : : }
172 : :
173 : : // Check that the files aren't empty, and then
174 : : // copy the cached C file to the destination
175 [ + - ][ + - ]: 372 : if (!get_file_size(fd_module) || !get_file_size(fd_c) ||
[ + - ][ + - ]
[ - + ][ - + ]
176 [ + - ]: 186 : !copy_file(c_src_path, s.translated_source))
177 : : {
178 [ # # ]: 0 : close(fd_module);
179 [ # # ]: 0 : close(fd_c);
180 : 0 : return false;
181 : : }
182 : :
183 : : // Copy the cached module to the destination (if needed)
184 [ + - ]: 186 : if (s.last_pass != 3)
185 : : {
186 [ + - ][ - + ]: 186 : if (!copy_file(s.hash_path, module_dest_path))
187 : : {
188 [ # # ]: 0 : unlink(c_src_path.c_str());
189 [ # # ]: 0 : close(fd_module);
190 [ # # ]: 0 : close(fd_c);
191 : 0 : return false;
192 : : }
193 : : // Copy the module signature file, if any.
194 : : // It is not an error if this fails.
195 [ + - ][ + - ]: 186 : if (file_exists (s.hash_path + ".sgn"))
[ + - ][ - + ]
196 [ # # ][ # # ]: 0 : copy_file(s.hash_path + ".sgn", module_dest_path + ".sgn");
[ # # ][ # # ]
[ # # ]
197 : : }
198 : :
199 : : // We're done with these file handles.
200 [ + - ]: 186 : close(fd_module);
201 [ + - ]: 186 : close(fd_c);
202 : :
203 : : // To preserve semantics (since this will happen if we're not
204 : : // caching), display the C source if the last pass is 3.
205 [ - + ]: 186 : if (s.last_pass == 3)
206 : : {
207 [ # # ][ # # ]: 0 : ifstream i (s.translated_source.c_str());
208 [ # # ][ # # ]: 0 : cout << i.rdbuf();
[ # # ]
209 : : }
210 : : // And similarly, display probe module name for -p4.
211 [ + + ]: 186 : if (s.last_pass == 4)
212 [ + - ][ + - ]: 23 : cout << s.hash_path << endl;
213 : :
214 : : // If everything worked, tell the user. We need to do this here,
215 : : // since if copying the cached C file works, but copying the cached
216 : : // module fails, we remove the cached C file and let the C file get
217 : : // regenerated.
218 : : // NB: don't use s.verbose here, since we're still in pass-2,
219 : : // i.e., s.verbose = s.perpass_verbose[1].
220 [ + + ]: 186 : if (s.perpass_verbose[2])
221 [ + - ][ + - ]: 8 : clog << _("Pass 3: using cached ") << c_src_path << endl;
[ + - ]
222 [ + + ][ + - ]: 186 : if (s.perpass_verbose[3] && s.last_pass != 3)
223 [ + - ][ + - ]: 8 : clog << _("Pass 4: using cached ") << s.hash_path << endl;
[ + - ]
224 : :
225 [ + - ][ + - ]: 186 : PROBE2(stap, cache__get, c_src_path.c_str(), s.hash_path.c_str());
226 : :
227 [ + - ][ + - ]: 814 : return true;
228 : : }
229 : :
230 : :
231 : : void
232 : 592 : clean_cache(systemtap_session& s)
233 : : {
234 [ + - ]: 592 : if (s.cache_path != "")
235 : : {
236 : : /* Get cache size limit from file in the stap cache dir */
237 [ + - ]: 592 : string cache_max_filename = s.cache_path + "/";
238 [ + - ]: 592 : cache_max_filename += SYSTEMTAP_CACHE_MAX_FILENAME;
239 [ + - ][ + - ]: 592 : ifstream cache_max_file(cache_max_filename.c_str(), ios::in);
240 : : unsigned long cache_mb_max;
241 : :
242 [ + - ][ + + ]: 592 : if (cache_max_file.is_open())
243 : : {
244 [ + - ]: 590 : cache_max_file >> cache_mb_max;
245 [ + - ]: 590 : cache_max_file.close();
246 : : }
247 : : else
248 : : {
249 : : //file doesnt exist, create a default size
250 [ + - ][ + - ]: 2 : ofstream default_cache_max(cache_max_filename.c_str(), ios::out);
251 [ + - ][ + - ]: 2 : default_cache_max << SYSTEMTAP_CACHE_DEFAULT_MB << endl;
252 : 2 : cache_mb_max = SYSTEMTAP_CACHE_DEFAULT_MB;
253 : :
254 [ + + ]: 2 : if (s.verbose > 1)
255 [ + - ][ + - ]: 2 : clog << _F("Cache limit file %s/%s missing, creating default.",
[ + - ][ + - ]
256 [ + - ][ + - ]: 3 : s.cache_path.c_str(), SYSTEMTAP_CACHE_MAX_FILENAME) << endl;
257 : : }
258 : :
259 : : /* Get cache clean interval from file in the stap cache dir */
260 [ + - ]: 592 : string cache_clean_interval_filename = s.cache_path + "/";
261 [ + - ]: 592 : cache_clean_interval_filename += SYSTEMTAP_CACHE_CLEAN_INTERVAL_FILENAME;
262 [ + - ][ + - ]: 592 : ifstream cache_clean_interval_file(cache_clean_interval_filename.c_str(), ios::in);
263 : : unsigned long cache_clean_interval;
264 : :
265 [ + - ][ + + ]: 592 : if (cache_clean_interval_file.is_open())
266 : : {
267 [ + - ]: 591 : cache_clean_interval_file >> cache_clean_interval;
268 [ + - ]: 591 : cache_clean_interval_file.close();
269 : : }
270 : : else
271 : : {
272 : : //file doesnt exist, create a default interval
273 [ + - ][ + - ]: 1 : ofstream default_cache_clean_interval(cache_clean_interval_filename.c_str(), ios::out);
274 [ + - ][ + - ]: 1 : default_cache_clean_interval << SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S << endl;
275 : 1 : cache_clean_interval = SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S;
276 : :
277 [ - + ]: 1 : if (s.verbose > 1)
278 [ # # ][ # # ]: 0 : clog << _F("Cache clean interval file %s missing, creating default.",
[ # # ][ # # ]
279 [ # # ][ + - ]: 1 : cache_clean_interval_filename.c_str())<< endl;
280 : : }
281 : :
282 : : /* Check the cache cleaning interval */
283 : : struct stat sb;
284 [ + - ][ - + ]: 592 : if(stat(cache_clean_interval_filename.c_str(), &sb) < 0)
285 : : {
286 : 0 : const char* e = strerror (errno);
287 [ # # ][ # # ]: 0 : cerr << _F("clean_cache stat error: %s", e) << endl;
[ # # ][ # # ]
288 : : return;
289 : : }
290 : :
291 : : struct timeval current_time;
292 : 592 : gettimeofday(¤t_time, NULL);
293 [ + + ]: 592 : if(difftime(current_time.tv_sec, sb.st_mtime) < cache_clean_interval)
294 : : {
295 : : //interval not passed, don't continue
296 [ + + ]: 564 : if (s.verbose > 1)
297 [ + - ][ + - ]: 579 : clog << _F("Cache cleaning skipped, interval not reached %lu s / %lu s.",
[ + - ]
298 [ + - ]: 15 : (current_time.tv_sec-sb.st_mtime), cache_clean_interval) << endl;
299 : : return;
300 : : }
301 : : else
302 : : {
303 : : //interval reached, continue
304 [ + + ]: 28 : if (s.verbose > 1)
305 [ + - ][ + - ]: 2 : clog << _F("Cleaning cache, interval reached %lu s > %lu s.",
[ + - ]
306 [ + - ]: 1 : (current_time.tv_sec-sb.st_mtime), cache_clean_interval) << endl;
307 : : }
308 : :
309 : : // glob for all files that look like hashes
310 : : glob_t cache_glob;
311 [ + - ]: 28 : ostringstream glob_pattern;
312 [ + - ][ + - ]: 28 : glob_pattern << s.cache_path << "/*/*";
313 [ + + ]: 924 : for (unsigned int i = 0; i < 32; i++)
314 [ + - ]: 896 : glob_pattern << "[[:xdigit:]]";
315 [ + - ]: 28 : glob_pattern << "*";
316 [ + - ][ + - ]: 28 : int rc = glob(glob_pattern.str().c_str(), 0, NULL, &cache_glob);
[ + - ]
317 [ - + ]: 28 : if (rc) {
318 [ # # ][ # # ]: 0 : cerr << _F("clean_cache glob error rc=%d", rc) << endl;
[ # # ][ # # ]
319 : : return;
320 : : }
321 : :
322 : : regex_t hash_len_re;
323 [ + - ]: 28 : rc = regcomp (&hash_len_re, "([[:xdigit:]]{32}_[[:digit:]]+)", REG_EXTENDED);
324 [ - + ]: 28 : if (rc) {
325 [ # # ][ # # ]: 0 : cerr << _F("clean_cache regcomp error rc=%d", rc) << endl;
[ # # ][ # # ]
326 : 0 : globfree(&cache_glob);
327 : : return;
328 : : }
329 : :
330 : : // group all files with the same HASH_LEN
331 [ + - ]: 28 : map<string, vector<string> > cache_groups;
332 [ + + ]: 37781 : for (size_t i = 0; i < cache_glob.gl_pathc; i++)
333 : : {
334 : 37753 : const char* path = cache_glob.gl_pathv[i];
335 : : regmatch_t hash_len;
336 [ + - ]: 37753 : rc = regexec(&hash_len_re, path, 1, &hash_len, 0);
337 [ + - ][ + - ]: 37753 : if (rc || hash_len.rm_so == -1 || hash_len.rm_eo == -1)
[ - + ]
338 [ # # ][ # # ]: 0 : cache_groups[path].push_back(path); // ungrouped
[ # # ][ # # ]
[ # # ][ # # ]
339 : : else
340 : : cache_groups[string(path + hash_len.rm_so,
341 [ + - ]: 75506 : hash_len.rm_eo - hash_len.rm_so)]
[ + - + - ]
342 [ + - ][ + - ]: 37753 : .push_back(path);
[ + - ]
343 : : }
344 [ + - ]: 28 : regfree(&hash_len_re);
345 : 28 : globfree(&cache_glob);
346 : :
347 : :
348 : : // create each cache entry and accumulate the sum
349 : 28 : off_t cache_size_b = 0;
350 [ + - ]: 28 : set<cache_ent_info> cache_contents;
351 [ + - ][ + + ]: 27886 : for (map<string, vector<string> >::const_iterator it = cache_groups.begin();
352 [ + - ]: 13943 : it != cache_groups.end(); ++it)
353 : : {
354 [ + - ][ + - ]: 13915 : cache_ent_info cur_info(it->second);
355 [ + - ][ + - ]: 13915 : if (cache_contents.insert(cur_info).second)
356 : 13915 : cache_size_b += cur_info.size;
357 [ + - ]: 13915 : }
358 : :
359 : 28 : unsigned long r_cache_size = cache_size_b;
360 [ + - ]: 28 : vector<const cache_ent_info*> removed;
361 : :
362 : : //unlink .ko and .c until the cache size is under the limit
363 [ + - ][ + - ]: 1328 : for (set<cache_ent_info>::iterator i = cache_contents.begin();
364 [ + - ]: 664 : i != cache_contents.end(); ++i)
365 : : {
366 [ + + ]: 664 : if (r_cache_size < cache_mb_max * 1024 * 1024) //convert cache_mb_max to bytes
367 : 28 : break;
368 : :
369 : : //remove this (*i) cache_entry, add to removed list
370 [ + - ][ + + ]: 2402 : for (size_t j = 0; j < i->paths.size(); ++j)
371 [ + - ][ + - ]: 1766 : PROBE1(stap, cache__clean, i->paths[j].c_str());
372 [ + - ][ + - ]: 636 : i->unlink();
373 [ + - ]: 636 : r_cache_size -= i->size;
374 [ + - ]: 636 : removed.push_back(&*i);
375 : : }
376 : :
377 [ + + ][ + - ]: 28 : if (s.verbose > 1 && !removed.empty())
[ - + ][ - + ]
378 : : {
379 [ # # ][ # # ]: 0 : clog << _("Cache cleaning successful, removed entries: ") << endl;
380 [ # # ]: 0 : for (size_t i = 0; i < removed.size(); ++i)
381 [ # # ]: 0 : for (size_t j = 0; j < removed[i]->paths.size(); ++j)
382 [ # # ][ # # ]: 0 : clog << " " << removed[i]->paths[j] << endl;
[ # # ]
383 : : }
384 : :
385 [ + - ][ - + ]: 28 : if(utime(cache_clean_interval_filename.c_str(), NULL)<0)
386 : : {
387 : 0 : const char* e = strerror (errno);
388 [ # # ][ # # ]: 28 : cerr << _F("clean_cache utime error: %s", e) << endl;
[ # # ][ # # ]
389 : : return;
390 [ + - ][ - + ]: 592 : }
[ + - ][ - + ]
[ + - ][ - + ]
[ + - ][ - + ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ + + ]
391 : : }
392 : : else
393 : : {
394 [ # # ]: 0 : if (s.verbose > 1)
395 : 592 : clog << _("Cache cleaning skipped, no cache path.") << endl;
396 : : }
397 : : }
398 : :
399 : :
400 : 13915 : cache_ent_info::cache_ent_info(const vector<string>& paths):
401 : 13915 : paths(paths), size(0), mtime(0)
402 : : {
403 : : struct stat file_info;
404 [ + + ]: 51668 : for (size_t i = 0; i < paths.size(); ++i)
405 [ + - ][ + - ]: 37753 : if (stat(paths[i].c_str(), &file_info) == 0)
406 : : {
407 : 37753 : size += file_info.st_size;
408 [ + + ]: 37753 : if (file_info.st_mtime > mtime)
409 : 18099 : mtime = file_info.st_mtime;
410 : : }
411 : 13915 : }
412 : :
413 : :
414 : : // The ordering here determines the order that
415 : : // files will be removed from the cache.
416 : : bool
417 : 136080 : cache_ent_info::operator<(const struct cache_ent_info& other) const
418 : : {
419 [ + + ]: 136080 : if (mtime != other.mtime)
420 : 122020 : return mtime < other.mtime;
421 [ + + ]: 14060 : if (size != other.size)
422 : 13971 : return size < other.size;
423 [ - + ]: 89 : if (paths.size() != other.paths.size())
424 : 0 : return paths.size() < other.paths.size();
425 [ + - ]: 89 : for (size_t i = 0; i < paths.size(); ++i)
426 [ + - ]: 89 : if (paths[i] != other.paths[i])
427 : 89 : return paths[i] < other.paths[i];
428 : 136080 : return false;
429 : : }
430 : :
431 : :
432 : : void
433 : 636 : cache_ent_info::unlink() const
434 : : {
435 [ + + ]: 2402 : for (size_t i = 0; i < paths.size(); ++i)
436 : 1766 : ::unlink(paths[i].c_str());
437 [ + - ][ + - ]: 7878 : }
438 : :
439 : :
440 : : /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
|