]> sourceware.org Git - systemtap.git/blob - cache.cxx
Consolidate task_finder/vma tracker initialization.
[systemtap.git] / cache.cxx
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 "sys/sdt.h"
14 #include <cerrno>
15 #include <string>
16 #include <fstream>
17 #include <cstring>
18 #include <cassert>
19
20 extern "C" {
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <glob.h>
25 }
26
27 using namespace std;
28
29
30 #define SYSTEMTAP_CACHE_MAX_FILENAME "cache_mb_limit"
31 #define SYSTEMTAP_CACHE_DEFAULT_MB 64
32
33 struct cache_ent_info {
34 string path;
35 bool is_module;
36 size_t size;
37 long weight; //lower == removed earlier
38
39 cache_ent_info(const string& path, bool is_module);
40 bool operator<(const struct cache_ent_info& other) const
41 { return weight < other.weight; }
42 void unlink() const;
43 };
44
45
46 void
47 add_stapconf_to_cache(systemtap_session& s)
48 {
49 bool verbose = s.verbose > 1;
50
51 string stapconf_src_path = s.tmpdir + "/" + s.stapconf_name;
52 if (!copy_file(stapconf_src_path, s.stapconf_path, verbose))
53 {
54 // NB: this is not so severe as to prevent reuse of the .ko
55 // already copied.
56 //
57 // s.use_script_cache = false;
58 // return;
59 }
60 }
61
62
63 void
64 add_script_to_cache(systemtap_session& s)
65 {
66 bool verbose = s.verbose > 1;
67
68 // PR10543: clean the cache *before* we try putting something new into it.
69 // We don't want to risk having the brand new contents being erased again.
70 clean_cache(s);
71
72 string module_src_path = s.tmpdir + "/" + s.module_name + ".ko";
73 STAP_PROBE2(stap, cache__add__module, module_src_path.c_str(), s.hash_path.c_str());
74 if (!copy_file(module_src_path, s.hash_path, verbose))
75 {
76 s.use_script_cache = false;
77 return;
78 }
79 // Copy the signature file, if any. It is not an error if this fails.
80 if (file_exists (module_src_path + ".sgn"))
81 copy_file(module_src_path + ".sgn", s.hash_path + ".sgn", verbose);
82
83 string c_dest_path = s.hash_path;
84 if (endswith(c_dest_path, ".ko"))
85 c_dest_path.resize(c_dest_path.size() - 3);
86 c_dest_path += ".c";
87
88 STAP_PROBE2(stap, cache__add__source, s.translated_source.c_str(), c_dest_path.c_str());
89 if (!copy_file(s.translated_source, c_dest_path, verbose))
90 {
91 // NB: this is not so severe as to prevent reuse of the .ko
92 // already copied.
93 //
94 // s.use_script_cache = false;
95 }
96 }
97
98
99 bool
100 get_stapconf_from_cache(systemtap_session& s)
101 {
102 if (s.poison_cache)
103 return false;
104
105 string stapconf_dest_path = s.tmpdir + "/" + s.stapconf_name;
106 int fd_stapconf;
107
108 // See if stapconf exists
109 fd_stapconf = open(s.stapconf_path.c_str(), O_RDONLY);
110 if (fd_stapconf == -1)
111 {
112 // It isn't in cache.
113 return false;
114 }
115
116 // Copy the stapconf header file to the destination
117 if (!copy_file(s.stapconf_path, stapconf_dest_path))
118 {
119 close(fd_stapconf);
120 return false;
121 }
122
123 // We're done with this file handle.
124 close(fd_stapconf);
125
126 if (s.verbose > 1)
127 clog << "Pass 4: using cached " << s.stapconf_path << endl;
128
129 return true;
130 }
131
132
133 bool
134 get_script_from_cache(systemtap_session& s)
135 {
136 if (s.poison_cache)
137 return false;
138
139 string module_dest_path = s.tmpdir + "/" + s.module_name + ".ko";
140 string c_src_path = s.hash_path;
141 int fd_module, fd_c;
142
143 if (endswith(c_src_path, ".ko"))
144 c_src_path.resize(c_src_path.size() - 3);
145 c_src_path += ".c";
146
147 // See if module exists
148 fd_module = open(s.hash_path.c_str(), O_RDONLY);
149 if (fd_module == -1)
150 {
151 // It isn't in cache.
152 return false;
153 }
154
155 // See if C file exists.
156 fd_c = open(c_src_path.c_str(), O_RDONLY);
157 if (fd_c == -1)
158 {
159 // The module is there, but the C file isn't. Cleanup and
160 // return.
161 close(fd_module);
162 unlink(s.hash_path.c_str());
163 return false;
164 }
165
166 // Copy the cached C file to the destination
167 if (!copy_file(c_src_path, s.translated_source))
168 {
169 close(fd_module);
170 close(fd_c);
171 return false;
172 }
173
174 // Copy the cached module to the destination (if needed)
175 if (s.last_pass != 3)
176 {
177 if (!copy_file(s.hash_path, module_dest_path))
178 {
179 unlink(c_src_path.c_str());
180 close(fd_module);
181 close(fd_c);
182 return false;
183 }
184 // Copy the module signature file, if any.
185 // It is not an error if this fails.
186 if (file_exists (s.hash_path + ".sgn"))
187 copy_file(s.hash_path + ".sgn", module_dest_path + ".sgn");
188 }
189
190 // We're done with these file handles.
191 close(fd_module);
192 close(fd_c);
193
194 // To preserve semantics (since this will happen if we're not
195 // caching), display the C source if the last pass is 3.
196 if (s.last_pass == 3)
197 {
198 ifstream i (s.translated_source.c_str());
199 cout << i.rdbuf();
200 }
201 // And similarly, display probe module name for -p4.
202 if (s.last_pass == 4)
203 cout << s.hash_path << endl;
204
205 // If everything worked, tell the user. We need to do this here,
206 // since if copying the cached C file works, but copying the cached
207 // module fails, we remove the cached C file and let the C file get
208 // regenerated.
209 // NB: don't use s.verbose here, since we're still in pass-2,
210 // i.e., s.verbose = s.perpass_verbose[1].
211 if (s.perpass_verbose[2])
212 clog << "Pass 3: using cached " << c_src_path << endl;
213 if (s.perpass_verbose[3] && s.last_pass != 3)
214 clog << "Pass 4: using cached " << s.hash_path << endl;
215
216 STAP_PROBE2(stap, cache__get, c_src_path.c_str(), s.hash_path.c_str());
217
218 return true;
219 }
220
221
222 void
223 clean_cache(systemtap_session& s)
224 {
225 if (s.cache_path != "")
226 {
227 /* Get cache size limit from file in the stap cache dir */
228 string cache_max_filename = s.cache_path + "/";
229 cache_max_filename += SYSTEMTAP_CACHE_MAX_FILENAME;
230 ifstream cache_max_file(cache_max_filename.c_str(), ios::in);
231 unsigned long cache_mb_max;
232
233 if (cache_max_file.is_open())
234 {
235 cache_max_file >> cache_mb_max;
236 cache_max_file.close();
237 }
238 else
239 {
240 //file doesnt exist, create a default size
241 ofstream default_cache_max(cache_max_filename.c_str(), ios::out);
242 default_cache_max << SYSTEMTAP_CACHE_DEFAULT_MB << endl;
243 cache_mb_max = SYSTEMTAP_CACHE_DEFAULT_MB;
244
245 if (s.verbose > 1)
246 clog << "Cache limit file " << s.cache_path << "/"
247 << SYSTEMTAP_CACHE_MAX_FILENAME
248 << " missing, creating default." << endl;
249 }
250
251 //glob for all kernel modules in the cache dir
252 glob_t cache_glob;
253 string glob_str = s.cache_path + "/*/*.ko";
254 glob(glob_str.c_str(), 0, NULL, &cache_glob);
255
256
257 set<struct cache_ent_info> cache_contents;
258 unsigned long cache_size_b = 0;
259
260 //grab info for each cache entry (.ko and .c)
261 for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
262 {
263 string cache_ent_path = cache_glob.gl_pathv[i];
264 cache_ent_path.resize(cache_ent_path.length() - 3);
265
266 struct cache_ent_info cur_info(cache_ent_path, true);
267 if (cur_info.size != 0 && cur_info.weight != 0)
268 {
269 cache_size_b += cur_info.size;
270 cache_contents.insert(cur_info);
271 }
272 }
273
274 globfree(&cache_glob);
275
276 //grab info for each typequery user module (.so)
277 glob_str = s.cache_path + "/*/*.so";
278 glob(glob_str.c_str(), 0, NULL, &cache_glob);
279 for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
280 {
281 string cache_ent_path = cache_glob.gl_pathv[i];
282 struct cache_ent_info cur_info(cache_ent_path, false);
283 if (cur_info.size != 0 && cur_info.weight != 0)
284 {
285 cache_size_b += cur_info.size;
286 cache_contents.insert(cur_info);
287 }
288 }
289
290 globfree(&cache_glob);
291
292 //grab info for each stapconf cache entry (.h)
293 glob_str = s.cache_path + "/*/*.h";
294 glob(glob_str.c_str(), 0, NULL, &cache_glob);
295 for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
296 {
297 string cache_ent_path = cache_glob.gl_pathv[i];
298 struct cache_ent_info cur_info(cache_ent_path, false);
299 if (cur_info.size != 0 && cur_info.weight != 0)
300 {
301 cache_size_b += cur_info.size;
302 cache_contents.insert(cur_info);
303 }
304 }
305
306 globfree(&cache_glob);
307
308 // grab info for each staphash log file (.log)
309 glob_str = s.cache_path + "/*/*.log";
310 glob(glob_str.c_str(), 0, NULL, &cache_glob);
311 for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
312 {
313 string cache_ent_path = cache_glob.gl_pathv[i];
314 struct cache_ent_info cur_info(cache_ent_path, false);
315 if (cur_info.size != 0 && cur_info.weight != 0)
316 {
317 cache_size_b += cur_info.size;
318 cache_contents.insert(cur_info);
319 }
320 }
321
322 globfree(&cache_glob);
323
324 set<struct cache_ent_info>::iterator i;
325 unsigned long r_cache_size = cache_size_b;
326 string removed_dirs = "";
327
328 //unlink .ko and .c until the cache size is under the limit
329 for (i = cache_contents.begin(); i != cache_contents.end(); ++i)
330 {
331 if ( (r_cache_size / 1024 / 1024) < cache_mb_max) //convert r_cache_size to MiB
332 break;
333
334 STAP_PROBE1(stap, cache__clean, (i->path).c_str());
335 //remove this (*i) cache_entry, add to removed list
336 i->unlink();
337 r_cache_size -= i->size;
338 removed_dirs += i->path + ", ";
339 }
340
341 cache_contents.clear();
342
343 if (s.verbose > 1 && removed_dirs != "")
344 {
345 //remove trailing ", "
346 removed_dirs = removed_dirs.substr(0, removed_dirs.length() - 2);
347 clog << "Cache cleaning successful, removed entries: "
348 << removed_dirs << endl;
349 }
350 }
351 else
352 {
353 if (s.verbose > 1)
354 clog << "Cache cleaning skipped, no cache path." << endl;
355 }
356 }
357
358 //Assign a weight for a particular file. A lower weight
359 // will be removed before a higher weight.
360 //TODO: for now use system mtime... later base a
361 // weighting on size, ctime, atime etc..
362 static long
363 get_file_weight(const string &path)
364 {
365 time_t dir_mtime = 0;
366 struct stat dir_stat_info;
367
368 if (stat(path.c_str(), &dir_stat_info) == 0)
369 //GNU struct stat defines st_atime as st_atim.tv_sec
370 // but it doesnt seem to work properly in practice
371 // so use st_atim.tv_sec -- bad for portability?
372 dir_mtime = dir_stat_info.st_mtim.tv_sec;
373
374 return dir_mtime;
375 }
376
377
378 cache_ent_info::cache_ent_info(const string& path, bool is_module):
379 path(path), is_module(is_module)
380 {
381 if (is_module)
382 {
383 string mod_path = path + ".ko";
384 string modsgn_path = path + ".ko.sgn";
385 string source_path = path + ".c";
386 string hash_path = path + ".log";
387 size = get_file_size(mod_path)
388 + get_file_size(modsgn_path);
389 + get_file_size(source_path);
390 + get_file_size(hash_path);
391 weight = get_file_weight(mod_path);
392 }
393 else
394 {
395 size = get_file_size(path);
396 weight = get_file_weight(path);
397 }
398 }
399
400
401 void
402 cache_ent_info::unlink() const
403 {
404 if (is_module)
405 {
406 string mod_path = path + ".ko";
407 string modsgn_path = path + ".ko.sgn";
408 string source_path = path + ".c";
409 string hash_path = path + ".log";
410 ::unlink(mod_path.c_str());
411 ::unlink(modsgn_path.c_str());
412 ::unlink(source_path.c_str());
413 ::unlink(hash_path.c_str());
414 }
415 else
416 ::unlink(path.c_str());
417 }
418
419 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.053479 seconds and 5 git commands to generate.