Example #1
0
// Zero all the stats structures.
void
stats_zero(void)
{
	assert(conf);

	char *fname = format("%s/stats", conf->cache_dir);
	x_unlink(fname);
	free(fname);

	time_t timestamp = time(NULL);

	for (int dir = 0; dir <= 0xF; dir++) {
		struct counters *counters = counters_init(STATS_END);
		struct stat st;
		fname = format("%s/%1x/stats", conf->cache_dir, dir);
		if (stat(fname, &st) != 0) {
			// No point in trying to reset the stats file if it doesn't exist.
			free(fname);
			continue;
		}
		if (lockfile_acquire(fname, lock_staleness_limit)) {
			stats_read(fname, counters);
			for (unsigned i = 0; stats_info[i].message; i++) {
				if (!(stats_info[i].flags & FLAG_NOZERO)) {
					counters->data[stats_info[i].stat] = 0;
				}
			}
			counters->data[STATS_ZEROTIMESTAMP] = timestamp;
			stats_write(fname, counters);
			lockfile_release(fname);
		}
		counters_free(counters);
		free(fname);
	}
}
Example #2
0
/* zero all the stats structures */
void
stats_zero(void)
{
	int dir;
	unsigned i;
	char *fname;

	fname = format("%s/stats", cache_dir);
	x_unlink(fname);
	free(fname);

	for (dir = 0; dir <= 0xF; dir++) {
		struct counters *counters = counters_init(STATS_END);
		fname = format("%s/%1x/stats", cache_dir, dir);
		if (lockfile_acquire(fname, lock_staleness_limit)) {
			stats_read(fname, counters);
			for (i = 0; stats_info[i].message; i++) {
				if (!(stats_info[i].flags & FLAG_NOZERO)) {
					counters->data[stats_info[i].stat] = 0;
				}
			}
			stats_write(fname, counters);
			lockfile_release(fname);
		}
		counters_free(counters);
		free(fname);
	}
}
Example #3
0
// Count directory cleanup run.
void
stats_add_cleanup(const char *dir, unsigned count)
{
	struct counters *counters = counters_init(STATS_END);
	char *statsfile = format("%s/stats", dir);
	if (lockfile_acquire(statsfile, lock_staleness_limit)) {
		stats_read(statsfile, counters);
		counters->data[STATS_NUMCLEANUPS] += count;
		stats_write(statsfile, counters);
		lockfile_release(statsfile);
	}
	free(statsfile);
	counters_free(counters);
}
Example #4
0
// Set the per-directory sizes.
void
stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size)
{
	struct counters *counters = counters_init(STATS_END);
	char *statsfile = format("%s/stats", dir);
	if (lockfile_acquire(statsfile, lock_staleness_limit)) {
		stats_read(statsfile, counters);
		counters->data[STATS_NUMFILES] = num_files;
		counters->data[STATS_TOTALSIZE] = total_size / 1024;
		stats_write(statsfile, counters);
		lockfile_release(statsfile);
	}
	free(statsfile);
	counters_free(counters);
}
Example #5
0
/* set the per directory limits */
int
stats_set_limits(long maxfiles, long maxsize)
{
	int dir;

	if (maxfiles != -1) {
		maxfiles /= 16;
	}
	if (maxsize != -1) {
		maxsize /= 16;
	}

	if (create_dir(cache_dir) != 0) {
		return 1;
	}

	/* set the limits in each directory */
	for (dir = 0; dir <= 0xF; dir++) {
		char *fname, *cdir;

		cdir = format("%s/%1x", cache_dir, dir);
		if (create_dir(cdir) != 0) {
			free(cdir);
			return 1;
		}
		fname = format("%s/stats", cdir);
		free(cdir);

		if (lockfile_acquire(fname, lock_staleness_limit)) {
			struct counters *counters = counters_init(STATS_END);
			stats_read(fname, counters);
			if (maxfiles != -1) {
				counters->data[STATS_MAXFILES] = maxfiles;
			}
			if (maxsize != -1) {
				counters->data[STATS_MAXSIZE] = maxsize;
			}
			stats_write(fname, counters);
			lockfile_release(fname);
			counters_free(counters);
		}
		free(fname);
	}

	return 0;
}
Example #6
0
int
main(int argc, char **argv)
{
	extern char *cache_logfile;
	cache_logfile = "/dev/stdout";
	if (argc == 4) {
		unsigned staleness_limit = atoi(argv[1]);
		if (str_eq(argv[2], "acquire")) {
			return lockfile_acquire(argv[3], staleness_limit) == 0;
		} else if (str_eq(argv[2], "release")) {
			lockfile_release(argv[3]);
			return 0;
		}
	}
	fprintf(stderr,
	        "Usage: testlockfile <staleness_limit> <acquire|release> <path>\n");
	return 1;
}
Example #7
0
// Write counter updates in counter_updates to disk.
void
stats_flush(void)
{
	assert(conf);

	if (!conf->stats) {
		return;
	}

	if (!counter_updates) {
		return;
	}

	bool should_flush = false;
	for (int i = 0; i < STATS_END; ++i) {
		if (counter_updates->data[i] > 0) {
			should_flush = true;
			break;
		}
	}
	if (!should_flush) {
		return;
	}

	if (!stats_file) {
		char *stats_dir;

		// A NULL stats_file means that we didn't get past calculate_object_hash(),
		// so we just choose one of stats files in the 16 subdirectories.
		stats_dir = format("%s/%x", conf->cache_dir, hash_from_int(getpid()) % 16);
		stats_file = format("%s/stats", stats_dir);
		free(stats_dir);
	}

	if (!lockfile_acquire(stats_file, lock_staleness_limit)) {
		return;
	}

	struct counters *counters = counters_init(STATS_END);
	stats_read(stats_file, counters);
	for (int i = 0; i < STATS_END; ++i) {
		counters->data[i] += counter_updates->data[i];
	}
	stats_write(stats_file, counters);
	lockfile_release(stats_file);

	if (!str_eq(conf->log_file, "") || conf->debug) {
		for (int i = 0; i < STATS_END; ++i) {
			if (counter_updates->data[stats_info[i].stat] != 0
			    && !(stats_info[i].flags & FLAG_NOZERO)) {
				cc_log("Result: %s", stats_info[i].message);
			}
		}
	}

	char *subdir = dirname(stats_file);
	bool need_cleanup = false;

	if (conf->max_files != 0
	    && counters->data[STATS_NUMFILES] > conf->max_files / 16) {
		cc_log("Need to clean up %s since it holds %u files (limit: %u files)",
		       subdir,
		       counters->data[STATS_NUMFILES],
		       conf->max_files / 16);
		need_cleanup = true;
	}
	if (conf->max_size != 0
	    && counters->data[STATS_TOTALSIZE] > conf->max_size / 1024 / 16) {
		cc_log("Need to clean up %s since it holds %u KiB (limit: %lu KiB)",
		       subdir,
		       counters->data[STATS_TOTALSIZE],
		       (unsigned long)conf->max_size / 1024 / 16);
		need_cleanup = true;
	}

	if (need_cleanup) {
		clean_up_dir(conf, subdir, conf->limit_multiple);
	}

	free(subdir);
	counters_free(counters);
}
Example #8
0
/*
 * Write counter updates in counter_updates to disk.
 */
void
stats_flush(void)
{
	struct counters *counters;
	bool need_cleanup = false;
	bool should_flush = false;
	int i;

	assert(conf);

	if (!conf->stats) {
		return;
	}

	if (!counter_updates) {
		return;
	}

	for (i = 0; i < STATS_END; ++i) {
		if (counter_updates->data[i] > 0) {
			should_flush = true;
			break;
		}
	}
	if (!should_flush) {
		return;
	}

	if (!stats_file) {
		char *stats_dir;

		/*
		 * A NULL stats_file means that we didn't get past calculate_object_hash(),
		 * so we just choose one of stats files in the 16 subdirectories.
		 */
		stats_dir = format("%s/%x", conf->cache_dir, hash_from_int(getpid()) % 16);
		stats_file = format("%s/stats", stats_dir);
		free(stats_dir);
	}

	if (!lockfile_acquire(stats_file, lock_staleness_limit)) {
		return;
	}
	counters = counters_init(STATS_END);
	stats_read(stats_file, counters);
	for (i = 0; i < STATS_END; ++i) {
		counters->data[i] += counter_updates->data[i];
	}
	stats_write(stats_file, counters);
	lockfile_release(stats_file);

	if (!str_eq(conf->log_file, "")) {
		for (i = 0; i < STATS_END; ++i) {
			if (counter_updates->data[stats_info[i].stat] != 0
			    && !(stats_info[i].flags & FLAG_NOZERO)) {
				cc_log("Result: %s", stats_info[i].message);
			}
		}
	}

	if (conf->max_files != 0
	    && counters->data[STATS_NUMFILES] > conf->max_files / 16) {
		need_cleanup = true;
	}
	if (conf->max_size != 0
	    && counters->data[STATS_TOTALSIZE] > conf->max_size / 1024 / 16) {
		need_cleanup = true;
	}

	if (need_cleanup) {
		char *p = dirname(stats_file);
		cleanup_dir(conf, p);
		free(p);
	}

	counters_free(counters);
}
Example #9
0
/*
 * Write counter updates in counter_updates to disk.
 */
void
stats_flush(void)
{
	struct counters *counters;
	bool need_cleanup = false;
	bool should_flush = false;
	int i;
	extern char *cache_logfile;

	if (getenv("CCACHE_NOSTATS")) return;

	init_counter_updates();

	for (i = 0; i < STATS_END; ++i) {
		if (counter_updates->data[i] > 0) {
			should_flush = true;
			break;
		}
	}
	if (!should_flush) return;

	if (!stats_file) {
		char *stats_dir;

		/*
		 * A NULL stats_file means that we didn't get past calculate_object_hash(),
		 * so we just choose one of stats files in the 16 subdirectories.
		 */
		if (!cache_dir) return;
		stats_dir = format("%s/%x", cache_dir, hash_from_int(getpid()) % 16);
		stats_file = format("%s/stats", stats_dir);
		create_dir(stats_dir);
		free(stats_dir);
	}

	if (!lockfile_acquire(stats_file, lock_staleness_limit)) {
		return;
	}
	counters = counters_init(STATS_END);
	stats_read(stats_file, counters);
	for (i = 0; i < STATS_END; ++i) {
		counters->data[i] += counter_updates->data[i];
	}
	stats_write(stats_file, counters);
	lockfile_release(stats_file);

	if (cache_logfile) {
		for (i = 0; i < STATS_END; ++i) {
			if (counter_updates->data[stats_info[i].stat] != 0
			    && !(stats_info[i].flags & FLAG_NOZERO)) {
				cc_log("Result: %s", stats_info[i].message);
			}
		}
	}

	if (counters->data[STATS_MAXFILES] != 0 &&
	    counters->data[STATS_NUMFILES] > counters->data[STATS_MAXFILES]) {
		need_cleanup = true;
	}
	if (counters->data[STATS_MAXSIZE] != 0 &&
	    counters->data[STATS_TOTALSIZE] > counters->data[STATS_MAXSIZE]) {
		need_cleanup = true;
	}

	if (need_cleanup) {
		char *p = dirname(stats_file);
		cleanup_dir(p,
		            counters->data[STATS_MAXFILES],
		            counters->data[STATS_MAXSIZE]);
		free(p);
	}
}
Example #10
0
/*
 * This function acquires a lockfile for the given path. Returns true if the
 * lock was acquired, otherwise false. If the lock has been considered stale
 * for the number of microseconds specified by staleness_limit, the function
 * will (if possible) break the lock and then try to acquire it again. The
 * staleness limit should be reasonably larger than the longest time the lock
 * can be expected to be held, and the updates of the locked path should
 * probably be made with an atomic rename(2) to avoid corruption in the rare
 * case that the lock is broken by another process.
 */
bool
lockfile_acquire(const char *path, unsigned staleness_limit)
{
	char *lockfile = format("%s.lock", path);
	char *my_content = NULL, *content = NULL, *initial_content = NULL;
	const char *hostname = get_hostname();
	bool acquired = false;
#ifdef _WIN32
	const size_t bufsize = 1024;
	int fd, len;
#else
	int ret;
#endif
	unsigned to_sleep = 1000, slept = 0; /* Microseconds. */

	while (1) {
		free(my_content);
		my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(NULL));

#ifdef _WIN32
		fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0666);
		if (fd == -1) {
			cc_log("lockfile_acquire: open WRONLY %s: %s", lockfile, strerror(errno));
			if (errno != EEXIST) {
				/* Directory doesn't exist or isn't writable? */
				goto out;
			}
			/* Someone else has the lock. */
			fd = open(lockfile, O_RDONLY|O_BINARY);
			if (fd == -1) {
				if (errno == ENOENT) {
					/*
					 * The file was removed after the open() call above, so retry
					 * acquiring it.
					 */
					continue;
				} else {
					cc_log("lockfile_acquire: open RDONLY %s: %s",
					       lockfile, strerror(errno));
					goto out;
				}
			}
			free(content);
			content = x_malloc(bufsize);
			if ((len = read(fd, content, bufsize - 1)) == -1) {
				cc_log("lockfile_acquire: read %s: %s", lockfile, strerror(errno));
				close(fd);
				goto out;
			}
			close(fd);
			content[len] = '\0';
		} else {
			/* We got the lock. */
			if (write(fd, my_content, strlen(my_content)) == -1) {
				cc_log("lockfile_acquire: write %s: %s", lockfile, strerror(errno));
				close(fd);
				unlink(lockfile);
				goto out;
			}
			close(fd);
			acquired = true;
			goto out;
		}
#else
		ret = symlink(my_content, lockfile);
		if (ret == 0) {
			/* We got the lock. */
			acquired = true;
			goto out;
		}
		cc_log("lockfile_acquire: symlink %s: %s", lockfile, strerror(errno));
		if (errno != EEXIST) {
			/* Directory doesn't exist or isn't writable? */
			goto out;
		}
		free(content);
		content = x_readlink(lockfile);
		if (!content) {
			if (errno == ENOENT) {
				/*
				 * The symlink was removed after the symlink() call above, so retry
				 * acquiring it.
				 */
				continue;
			} else {
				cc_log("lockfile_acquire: readlink %s: %s", lockfile, strerror(errno));
				goto out;
			}
		}
#endif

		if (str_eq(content, my_content)) {
			/* Lost NFS reply? */
			cc_log("lockfile_acquire: symlink %s failed but we got the lock anyway",
			       lockfile);
			acquired = true;
			goto out;
		}
		/*
		 * A possible improvement here would be to check if the process holding the
		 * lock is still alive and break the lock early if it isn't.
		 */
		cc_log("lockfile_acquire: lock info for %s: %s", lockfile, content);
		if (!initial_content) {
			initial_content = x_strdup(content);
		}
		if (slept > staleness_limit) {
			if (str_eq(content, initial_content)) {
				/* The lock seems to be stale -- break it. */
				cc_log("lockfile_acquire: breaking %s", lockfile);
				if (lockfile_acquire(lockfile, staleness_limit)) {
					lockfile_release(path);
					lockfile_release(lockfile);
					to_sleep = 1000;
					slept = 0;
					continue;
				}
			}
			cc_log("lockfile_acquire: gave up acquiring %s", lockfile);
			goto out;
		}
		cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds",
		       lockfile, to_sleep);
		usleep(to_sleep);
		slept += to_sleep;
		to_sleep *= 2;
	}

out:
	if (acquired) {
		cc_log("Acquired lock %s", lockfile);
	} else {
		cc_log("Failed to acquire lock %s", lockfile);
	}
	free(lockfile);
	free(my_content);
	free(initial_content);
	free(content);
	return acquired;
}