PageRenderTime 79ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/systemtap-1.8/cache.cxx

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