]> sourceware.org Git - systemtap.git/blame - cache.cxx
configure: Remove explicit msgfmt check
[systemtap.git] / cache.cxx
CommitLineData
9abec538 1// systemtap cache manager
98f552c2 2// Copyright (C) 2006-2009 Red Hat Inc.
9abec538
FCE
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
98f552c2 9#include "config.h"
1b78aef5
DS
10#include "session.h"
11#include "cache.h"
12#include "util.h"
0f5d597d 13#include "stap-probe.h"
1b78aef5
DS
14#include <cerrno>
15#include <string>
d1b3549d 16#include <fstream>
f74fb737 17#include <cstring>
4c797c5e 18#include <cassert>
8cc3cc33
JS
19#include <sstream>
20#include <vector>
1b78aef5
DS
21
22extern "C" {
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
06c7e057 26#include <glob.h>
8cc3cc33 27#include <regex.h>
dc7f0a09
CM
28#include <utime.h>
29#include <sys/time.h>
47caa991 30#include <unistd.h>
1b78aef5
DS
31}
32
33using namespace std;
34
35
de0db58a 36#define SYSTEMTAP_CACHE_MAX_FILENAME "cache_mb_limit"
f0220f40 37#define SYSTEMTAP_CACHE_DEFAULT_MB 256
dc7f0a09 38#define SYSTEMTAP_CACHE_CLEAN_INTERVAL_FILENAME "cache_clean_interval_s"
9892f8aa 39#define SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S 300
de0db58a
JS
40
41struct cache_ent_info {
8cc3cc33
JS
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;
de0db58a
JS
48 void unlink() const;
49};
50
de0db58a 51
1b78aef5 52void
9b3c54b2 53add_stapconf_to_cache(systemtap_session& s)
1b78aef5 54{
e16dc041
JS
55 bool verbose = s.verbose > 1;
56
de0db58a 57 string stapconf_src_path = s.tmpdir + "/" + s.stapconf_name;
e16dc041 58 if (!copy_file(stapconf_src_path, s.stapconf_path, verbose))
de0db58a 59 {
63d530ab
JS
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;
de0db58a 65 }
9b3c54b2
JS
66}
67
68
69void
70add_script_to_cache(systemtap_session& s)
71{
72 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 clean_cache(s);
de0db58a 77
4441e344 78 string module_src_path = s.tmpdir + "/" + s.module_filename();
0f5d597d 79 PROBE2(stap, cache__add__module, module_src_path.c_str(), s.hash_path.c_str());
e16dc041 80 if (!copy_file(module_src_path, s.hash_path, verbose))
1b78aef5 81 {
63d530ab 82 s.use_script_cache = false;
1b78aef5
DS
83 return;
84 }
64211010
DB
85 // Copy the signature file, if any. It is not an error if this fails.
86 if (file_exists (module_src_path + ".sgn"))
87 copy_file(module_src_path + ".sgn", s.hash_path + ".sgn", verbose);
1b78aef5
DS
88
89 string c_dest_path = s.hash_path;
4441e344 90 if (endswith(c_dest_path, ".ko") || endswith(c_dest_path, ".so"))
3a894f7e 91 c_dest_path.resize(c_dest_path.size() - 3);
1b78aef5
DS
92 c_dest_path += ".c";
93
0f5d597d 94 PROBE2(stap, cache__add__source, s.translated_source.c_str(), c_dest_path.c_str());
e16dc041 95 if (!copy_file(s.translated_source, c_dest_path, verbose))
1b78aef5 96 {
f4d5049b
FCE
97 // NB: this is not so severe as to prevent reuse of the .ko
98 // already copied.
99 //
63d530ab 100 // s.use_script_cache = false;
1b78aef5
DS
101 }
102}
103
104
105bool
9b3c54b2 106get_stapconf_from_cache(systemtap_session& s)
1b78aef5 107{
d105f664
JS
108 if (s.poison_cache)
109 return false;
110
de0db58a 111 string stapconf_dest_path = s.tmpdir + "/" + s.stapconf_name;
9b3c54b2 112 int fd_stapconf;
1b78aef5 113
de0db58a
JS
114 // See if stapconf exists
115 fd_stapconf = open(s.stapconf_path.c_str(), O_RDONLY);
116 if (fd_stapconf == -1)
117 {
118 // It isn't in cache.
119 return false;
120 }
121
122 // Copy the stapconf header file to the destination
a5751672
JS
123 if (!get_file_size(fd_stapconf) ||
124 !copy_file(s.stapconf_path, stapconf_dest_path))
de0db58a 125 {
de0db58a
JS
126 close(fd_stapconf);
127 return false;
128 }
129
130 // We're done with this file handle.
131 close(fd_stapconf);
132
133 if (s.verbose > 1)
18d6fad8 134 clog << _("Pass 4: using cached ") << s.stapconf_path << endl;
9b3c54b2
JS
135
136 return true;
137}
138
139
140bool
141get_script_from_cache(systemtap_session& s)
142{
d105f664
JS
143 if (s.poison_cache)
144 return false;
145
4441e344 146 string module_dest_path = s.tmpdir + "/" + s.module_filename();
9b3c54b2
JS
147 string c_src_path = s.hash_path;
148 int fd_module, fd_c;
149
4441e344 150 if (endswith(c_src_path, ".ko") || endswith(c_src_path, ".so"))
9b3c54b2
JS
151 c_src_path.resize(c_src_path.size() - 3);
152 c_src_path += ".c";
de0db58a 153
1b78aef5
DS
154 // See if module exists
155 fd_module = open(s.hash_path.c_str(), O_RDONLY);
156 if (fd_module == -1)
157 {
158 // It isn't in cache.
159 return false;
160 }
161
162 // See if C file exists.
163 fd_c = open(c_src_path.c_str(), O_RDONLY);
164 if (fd_c == -1)
165 {
166 // The module is there, but the C file isn't. Cleanup and
167 // return.
168 close(fd_module);
169 unlink(s.hash_path.c_str());
170 return false;
171 }
172
a5751672
JS
173 // Check that the files aren't empty, and then
174 // copy the cached C file to the destination
175 if (!get_file_size(fd_module) || !get_file_size(fd_c) ||
176 !copy_file(c_src_path, s.translated_source))
1b78aef5 177 {
1b78aef5
DS
178 close(fd_module);
179 close(fd_c);
180 return false;
181 }
182
183 // Copy the cached module to the destination (if needed)
184 if (s.last_pass != 3)
185 {
e16dc041 186 if (!copy_file(s.hash_path, module_dest_path))
1b78aef5 187 {
1b78aef5
DS
188 unlink(c_src_path.c_str());
189 close(fd_module);
190 close(fd_c);
191 return false;
192 }
64211010
DB
193 // Copy the module signature file, if any.
194 // It is not an error if this fails.
195 if (file_exists (s.hash_path + ".sgn"))
196 copy_file(s.hash_path + ".sgn", module_dest_path + ".sgn");
1b78aef5
DS
197 }
198
d1b3549d
DS
199 // We're done with these file handles.
200 close(fd_module);
201 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 if (s.last_pass == 3)
206 {
207 ifstream i (s.translated_source.c_str());
208 cout << i.rdbuf();
209 }
9abec538
FCE
210 // And similarly, display probe module name for -p4.
211 if (s.last_pass == 4)
212 cout << s.hash_path << endl;
d1b3549d 213
1b78aef5
DS
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.
aabba858
FCE
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 if (s.perpass_verbose[2])
18d6fad8 221 clog << _("Pass 3: using cached ") << c_src_path << endl;
aabba858 222 if (s.perpass_verbose[3] && s.last_pass != 3)
18d6fad8 223 clog << _("Pass 4: using cached ") << s.hash_path << endl;
1b78aef5 224
0f5d597d 225 PROBE2(stap, cache__get, c_src_path.c_str(), s.hash_path.c_str());
ce91eebd 226
1b78aef5
DS
227 return true;
228}
06c7e057
KS
229
230
d105f664 231void
06c7e057
KS
232clean_cache(systemtap_session& s)
233{
234 if (s.cache_path != "")
235 {
236 /* Get cache size limit from file in the stap cache dir */
237 string cache_max_filename = s.cache_path + "/";
238 cache_max_filename += SYSTEMTAP_CACHE_MAX_FILENAME;
239 ifstream cache_max_file(cache_max_filename.c_str(), ios::in);
fffd8e13 240 unsigned long cache_mb_max;
06c7e057
KS
241
242 if (cache_max_file.is_open())
243 {
fffd8e13 244 cache_max_file >> cache_mb_max;
06c7e057 245 cache_max_file.close();
06c7e057
KS
246 }
247 else
248 {
f604f54a 249 //file doesnt exist, create a default size
4e06b5e2
JS
250 ofstream default_cache_max(cache_max_filename.c_str(), ios::out);
251 default_cache_max << SYSTEMTAP_CACHE_DEFAULT_MB << endl;
252 cache_mb_max = SYSTEMTAP_CACHE_DEFAULT_MB;
06c7e057 253
f604f54a 254 if (s.verbose > 1)
46a1a151
JS
255 clog << _F("Cache limit file %s/%s missing, creating default.",
256 s.cache_path.c_str(), SYSTEMTAP_CACHE_MAX_FILENAME) << endl;
06c7e057
KS
257 }
258
dc7f0a09
CM
259 /* Get cache clean interval from file in the stap cache dir */
260 string cache_clean_interval_filename = s.cache_path + "/";
261 cache_clean_interval_filename += SYSTEMTAP_CACHE_CLEAN_INTERVAL_FILENAME;
262 ifstream cache_clean_interval_file(cache_clean_interval_filename.c_str(), ios::in);
263 unsigned long cache_clean_interval;
264
265 if (cache_clean_interval_file.is_open())
266 {
267 cache_clean_interval_file >> cache_clean_interval;
268 cache_clean_interval_file.close();
269 }
270 else
271 {
272 //file doesnt exist, create a default interval
273 ofstream default_cache_clean_interval(cache_clean_interval_filename.c_str(), ios::out);
274 default_cache_clean_interval << SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S << endl;
275 cache_clean_interval = SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S;
276
277 if (s.verbose > 1)
278 clog << _F("Cache clean interval file %s missing, creating default.",
279 cache_clean_interval_filename.c_str())<< endl;
280 }
281
282 /* Check the cache cleaning interval */
283 struct stat sb;
284 if(stat(cache_clean_interval_filename.c_str(), &sb) < 0)
285 {
286 const char* e = strerror (errno);
287 cerr << _F("clean_cache stat error: %s", e) << endl;
288 return;
289 }
290
291 struct timeval current_time;
292 gettimeofday(&current_time, NULL);
36fca3d6 293 if(difftime(current_time.tv_sec, sb.st_mtime) < cache_clean_interval)
dc7f0a09
CM
294 {
295 //interval not passed, don't continue
296 if (s.verbose > 1)
297 clog << _F("Cache cleaning skipped, interval not reached %lu s / %lu s.",
298 (current_time.tv_sec-sb.st_mtime), cache_clean_interval) << endl;
299 return;
300 }
301 else
302 {
303 //interval reached, continue
304 if (s.verbose > 1)
305 clog << _F("Cleaning cache, interval reached %lu s > %lu s.",
306 (current_time.tv_sec-sb.st_mtime), cache_clean_interval) << endl;
307 }
308
8cc3cc33 309 // glob for all files that look like hashes
06c7e057 310 glob_t cache_glob;
8cc3cc33
JS
311 ostringstream glob_pattern;
312 glob_pattern << s.cache_path << "/*/*";
313 for (unsigned int i = 0; i < 32; i++)
314 glob_pattern << "[[:xdigit:]]";
315 glob_pattern << "*";
316 int rc = glob(glob_pattern.str().c_str(), 0, NULL, &cache_glob);
317 if (rc) {
318 cerr << _F("clean_cache glob error rc=%d", rc) << endl;
319 return;
320 }
321
322 regex_t hash_len_re;
323 rc = regcomp (&hash_len_re, "([[:xdigit:]]{32}_[[:digit:]]+)", REG_EXTENDED);
324 if (rc) {
325 cerr << _F("clean_cache regcomp error rc=%d", rc) << endl;
326 globfree(&cache_glob);
327 return;
328 }
329
330 // group all files with the same HASH_LEN
331 map<string, vector<string> > cache_groups;
332 for (size_t i = 0; i < cache_glob.gl_pathc; i++)
06c7e057 333 {
8cc3cc33
JS
334 const char* path = cache_glob.gl_pathv[i];
335 regmatch_t hash_len;
336 rc = regexec(&hash_len_re, path, 1, &hash_len, 0);
337 if (rc || hash_len.rm_so == -1 || hash_len.rm_eo == -1)
338 cache_groups[path].push_back(path); // ungrouped
339 else
340 cache_groups[string(path + hash_len.rm_so,
341 hash_len.rm_eo - hash_len.rm_so)]
342 .push_back(path);
de0db58a 343 }
8cc3cc33 344 regfree(&hash_len_re);
de0db58a 345 globfree(&cache_glob);
06c7e057 346
2ca81875 347
8cc3cc33
JS
348 // create each cache entry and accumulate the sum
349 off_t cache_size_b = 0;
350 set<cache_ent_info> cache_contents;
351 for (map<string, vector<string> >::const_iterator it = cache_groups.begin();
352 it != cache_groups.end(); ++it)
de0db58a 353 {
8cc3cc33
JS
354 cache_ent_info cur_info(it->second);
355 if (cache_contents.insert(cur_info).second)
356 cache_size_b += cur_info.size;
06c7e057
KS
357 }
358
fffd8e13 359 unsigned long r_cache_size = cache_size_b;
8cc3cc33 360 vector<const cache_ent_info*> removed;
06c7e057
KS
361
362 //unlink .ko and .c until the cache size is under the limit
8cc3cc33
JS
363 for (set<cache_ent_info>::iterator i = cache_contents.begin();
364 i != cache_contents.end(); ++i)
06c7e057 365 {
8cc3cc33 366 if (r_cache_size < cache_mb_max * 1024 * 1024) //convert cache_mb_max to bytes
06c7e057
KS
367 break;
368
fffd8e13 369 //remove this (*i) cache_entry, add to removed list
8cc3cc33
JS
370 for (size_t j = 0; j < i->paths.size(); ++j)
371 PROBE1(stap, cache__clean, i->paths[j].c_str());
de0db58a 372 i->unlink();
fffd8e13 373 r_cache_size -= i->size;
8cc3cc33 374 removed.push_back(&*i);
06c7e057
KS
375 }
376
8cc3cc33 377 if (s.verbose > 1 && !removed.empty())
06c7e057 378 {
8cc3cc33
JS
379 clog << _("Cache cleaning successful, removed entries: ") << endl;
380 for (size_t i = 0; i < removed.size(); ++i)
381 for (size_t j = 0; j < removed[i]->paths.size(); ++j)
382 clog << " " << removed[i]->paths[j] << endl;
06c7e057 383 }
dc7f0a09
CM
384
385 if(utime(cache_clean_interval_filename.c_str(), NULL)<0)
386 {
387 const char* e = strerror (errno);
388 cerr << _F("clean_cache utime error: %s", e) << endl;
389 return;
390 }
06c7e057
KS
391 }
392 else
393 {
394 if (s.verbose > 1)
18d6fad8 395 clog << _("Cache cleaning skipped, no cache path.") << endl;
06c7e057
KS
396 }
397}
398
06c7e057 399
8cc3cc33
JS
400cache_ent_info::cache_ent_info(const vector<string>& paths):
401 paths(paths), size(0), mtime(0)
402{
403 struct stat file_info;
404 for (size_t i = 0; i < paths.size(); ++i)
405 if (stat(paths[i].c_str(), &file_info) == 0)
406 {
407 size += file_info.st_size;
408 if (file_info.st_mtime > mtime)
409 mtime = file_info.st_mtime;
410 }
06c7e057
KS
411}
412
413
8cc3cc33
JS
414// The ordering here determines the order that
415// files will be removed from the cache.
416bool
417cache_ent_info::operator<(const struct cache_ent_info& other) const
06c7e057 418{
8cc3cc33
JS
419 if (mtime != other.mtime)
420 return mtime < other.mtime;
421 if (size != other.size)
422 return size < other.size;
423 if (paths.size() != other.paths.size())
424 return paths.size() < other.paths.size();
425 for (size_t i = 0; i < paths.size(); ++i)
426 if (paths[i] != other.paths[i])
427 return paths[i] < other.paths[i];
428 return false;
de0db58a 429}
06c7e057 430
de0db58a
JS
431
432void
433cache_ent_info::unlink() const
434{
8cc3cc33
JS
435 for (size_t i = 0; i < paths.size(); ++i)
436 ::unlink(paths[i].c_str());
06c7e057 437}
73267b89 438
8cc3cc33 439
73267b89 440/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.188566 seconds and 5 git commands to generate.