cache.cxx - systemtap
Data types defined
Functions defined
Macros defined
Source code
#include "config.h"
#include "session.h"
#include "cache.h"
#include "util.h"
#include "stap-probe.h"
#include <cerrno>
#include <string>
#include <fstream>
#include <cstring>
#include <cassert>
#include <sstream>
#include <vector>
extern "C" {
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glob.h>
#include <regex.h>
#include <utime.h>
#include <sys/time.h>
#include <unistd.h>
}
using namespace std;
#define SYSTEMTAP_CACHE_MAX_FILENAME "cache_mb_limit"
#define SYSTEMTAP_CACHE_DEFAULT_MB 256
#define SYSTEMTAP_CACHE_CLEAN_INTERVAL_FILENAME "cache_clean_interval_s"
#define SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S 300
struct cache_ent_info {
vector<string> paths;
off_t size; time_t mtime;
cache_ent_info(const vector<string>& paths);
bool operator<(const struct cache_ent_info& other) const;
void unlink() const;
};
void
add_stapconf_to_cache(systemtap_session& s)
{
bool verbose = s.verbose > 1;
string stapconf_src_path = s.tmpdir + "/" + s.stapconf_name;
if (!copy_file(stapconf_src_path, s.stapconf_path, verbose))
{
}
}
void
add_script_to_cache(systemtap_session& s)
{
bool verbose = s.verbose > 1;
clean_cache(s);
string module_src_path = s.tmpdir + "/" + s.module_filename();
PROBE2(stap, cache__add__module, module_src_path.c_str(), s.hash_path.c_str());
if (!copy_file(module_src_path, s.hash_path, verbose))
{
s.use_script_cache = false;
return;
}
if (file_exists (module_src_path + ".sgn"))
copy_file(module_src_path + ".sgn", s.hash_path + ".sgn", verbose);
string c_dest_path = s.hash_path;
if (endswith(c_dest_path, ".ko") || endswith(c_dest_path, ".so"))
c_dest_path.resize(c_dest_path.size() - 3);
c_dest_path += ".c";
PROBE2(stap, cache__add__source, s.translated_source.c_str(), c_dest_path.c_str());
if (!copy_file(s.translated_source, c_dest_path, verbose))
{
}
}
bool
get_stapconf_from_cache(systemtap_session& s)
{
if (s.poison_cache)
return false;
string stapconf_dest_path = s.tmpdir + "/" + s.stapconf_name;
int fd_stapconf;
fd_stapconf = open(s.stapconf_path.c_str(), O_RDONLY);
if (fd_stapconf == -1)
{
return false;
}
if (!get_file_size(fd_stapconf) ||
!copy_file(s.stapconf_path, stapconf_dest_path))
{
close(fd_stapconf);
return false;
}
close(fd_stapconf);
if (s.verbose > 1)
clog << _("Pass 4: using cached ") << s.stapconf_path << endl;
return true;
}
bool
get_script_from_cache(systemtap_session& s)
{
if (s.poison_cache)
return false;
string module_dest_path = s.tmpdir + "/" + s.module_filename();
string c_src_path = s.hash_path;
int fd_module, fd_c;
if (endswith(c_src_path, ".ko") || endswith(c_src_path, ".so"))
c_src_path.resize(c_src_path.size() - 3);
c_src_path += ".c";
fd_module = open(s.hash_path.c_str(), O_RDONLY);
if (fd_module == -1)
{
return false;
}
fd_c = open(c_src_path.c_str(), O_RDONLY);
if (fd_c == -1)
{
close(fd_module);
unlink(s.hash_path.c_str());
return false;
}
if (!get_file_size(fd_module) || !get_file_size(fd_c) ||
!copy_file(c_src_path, s.translated_source))
{
close(fd_module);
close(fd_c);
return false;
}
if (s.last_pass != 3)
{
if (!copy_file(s.hash_path, module_dest_path))
{
unlink(c_src_path.c_str());
close(fd_module);
close(fd_c);
return false;
}
if (file_exists (s.hash_path + ".sgn"))
copy_file(s.hash_path + ".sgn", module_dest_path + ".sgn");
}
close(fd_module);
close(fd_c);
if (s.last_pass == 3)
{
ifstream i (s.translated_source.c_str());
cout << i.rdbuf();
}
if (s.last_pass == 4)
cout << s.hash_path << endl;
if (s.perpass_verbose[2])
clog << _("Pass 3: using cached ") << c_src_path << endl;
if (s.perpass_verbose[3] && s.last_pass != 3)
clog << _("Pass 4: using cached ") << s.hash_path << endl;
PROBE2(stap, cache__get, c_src_path.c_str(), s.hash_path.c_str());
return true;
}
void
clean_cache(systemtap_session& s)
{
if (s.cache_path != "")
{
string cache_max_filename = s.cache_path + "/";
cache_max_filename += SYSTEMTAP_CACHE_MAX_FILENAME;
ifstream cache_max_file(cache_max_filename.c_str(), ios::in);
unsigned long cache_mb_max;
if (cache_max_file.is_open())
{
cache_max_file >> cache_mb_max;
cache_max_file.close();
}
else
{
ofstream default_cache_max(cache_max_filename.c_str(), ios::out);
default_cache_max << SYSTEMTAP_CACHE_DEFAULT_MB << endl;
cache_mb_max = SYSTEMTAP_CACHE_DEFAULT_MB;
if (s.verbose > 1)
clog << _F("Cache limit file %s/%s missing, creating default.",
s.cache_path.c_str(), SYSTEMTAP_CACHE_MAX_FILENAME) << endl;
}
string cache_clean_interval_filename = s.cache_path + "/";
cache_clean_interval_filename += SYSTEMTAP_CACHE_CLEAN_INTERVAL_FILENAME;
ifstream cache_clean_interval_file(cache_clean_interval_filename.c_str(), ios::in);
unsigned long cache_clean_interval;
if (cache_clean_interval_file.is_open())
{
cache_clean_interval_file >> cache_clean_interval;
cache_clean_interval_file.close();
}
else
{
ofstream default_cache_clean_interval(cache_clean_interval_filename.c_str(), ios::out);
default_cache_clean_interval << SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S << endl;
cache_clean_interval = SYSTEMTAP_CACHE_CLEAN_DEFAULT_INTERVAL_S;
if (s.verbose > 1)
clog << _F("Cache clean interval file %s missing, creating default.",
cache_clean_interval_filename.c_str())<< endl;
}
struct stat sb;
if(stat(cache_clean_interval_filename.c_str(), &sb) < 0)
{
const char* e = strerror (errno);
cerr << _F("clean_cache stat error: %s", e) << endl;
return;
}
struct timeval current_time;
gettimeofday(¤t_time, NULL);
if(difftime(current_time.tv_sec, sb.st_mtime) < cache_clean_interval)
{
if (s.verbose > 1)
clog << _F("Cache cleaning skipped, interval not reached %lu s / %lu s.",
(current_time.tv_sec-sb.st_mtime), cache_clean_interval) << endl;
return;
}
else
{
if (s.verbose > 1)
clog << _F("Cleaning cache, interval reached %lu s > %lu s.",
(current_time.tv_sec-sb.st_mtime), cache_clean_interval) << endl;
}
glob_t cache_glob;
ostringstream glob_pattern;
glob_pattern << s.cache_path << "/*/*";
for (unsigned int i = 0; i < 32; i++)
glob_pattern << "[[:xdigit:]]";
glob_pattern << "*";
int rc = glob(glob_pattern.str().c_str(), 0, NULL, &cache_glob);
if (rc) {
cerr << _F("clean_cache glob error rc=%d", rc) << endl;
return;
}
regex_t hash_len_re;
rc = regcomp (&hash_len_re, "([[:xdigit:]]{32}_[[:digit:]]+)", REG_EXTENDED);
if (rc) {
cerr << _F("clean_cache regcomp error rc=%d", rc) << endl;
globfree(&cache_glob);
return;
}
map<string, vector<string> > cache_groups;
for (size_t i = 0; i < cache_glob.gl_pathc; i++)
{
const char* path = cache_glob.gl_pathv[i];
regmatch_t hash_len;
rc = regexec(&hash_len_re, path, 1, &hash_len, 0);
if (rc || hash_len.rm_so == -1 || hash_len.rm_eo == -1)
cache_groups[path].push_back(path); else
cache_groups[string(path + hash_len.rm_so,
hash_len.rm_eo - hash_len.rm_so)]
.push_back(path);
}
regfree(&hash_len_re);
globfree(&cache_glob);
off_t cache_size_b = 0;
set<cache_ent_info> cache_contents;
for (map<string, vector<string> >::const_iterator it = cache_groups.begin();
it != cache_groups.end(); ++it)
{
cache_ent_info cur_info(it->second);
if (cache_contents.insert(cur_info).second)
cache_size_b += cur_info.size;
}
unsigned long r_cache_size = cache_size_b;
vector<const cache_ent_info*> removed;
for (set<cache_ent_info>::iterator i = cache_contents.begin();
i != cache_contents.end(); ++i)
{
if (r_cache_size < cache_mb_max * 1024 * 1024) break;
for (size_t j = 0; j < i->paths.size(); ++j)
PROBE1(stap, cache__clean, i->paths[j].c_str());
i->unlink();
r_cache_size -= i->size;
removed.push_back(&*i);
}
if (s.verbose > 1 && !removed.empty())
{
clog << _("Cache cleaning successful, removed entries: ") << endl;
for (size_t i = 0; i < removed.size(); ++i)
for (size_t j = 0; j < removed[i]->paths.size(); ++j)
clog << " " << removed[i]->paths[j] << endl;
}
if(utime(cache_clean_interval_filename.c_str(), NULL)<0)
{
const char* e = strerror (errno);
cerr << _F("clean_cache utime error: %s", e) << endl;
return;
}
}
else
{
if (s.verbose > 1)
clog << _("Cache cleaning skipped, no cache path.") << endl;
}
}
cache_ent_info::cache_ent_info(const vector<string>& paths):
paths(paths), size(0), mtime(0)
{
struct stat file_info;
for (size_t i = 0; i < paths.size(); ++i)
if (stat(paths[i].c_str(), &file_info) == 0)
{
size += file_info.st_size;
if (file_info.st_mtime > mtime)
mtime = file_info.st_mtime;
}
}
bool
cache_ent_info::operator<(const struct cache_ent_info& other) const
{
if (mtime != other.mtime)
return mtime < other.mtime;
if (size != other.size)
return size < other.size;
if (paths.size() != other.paths.size())
return paths.size() < other.paths.size();
for (size_t i = 0; i < paths.size(); ++i)
if (paths[i] != other.paths[i])
return paths[i] < other.paths[i];
return false;
}
void
cache_ent_info::unlink() const
{
for (size_t i = 0; i < paths.size(); ++i)
::unlink(paths[i].c_str());
}