summaryrefslogtreecommitdiff
path: root/CCache/stats.c
diff options
context:
space:
mode:
Diffstat (limited to 'CCache/stats.c')
-rw-r--r--CCache/stats.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/CCache/stats.c b/CCache/stats.c
new file mode 100644
index 000000000..92bc4a835
--- /dev/null
+++ b/CCache/stats.c
@@ -0,0 +1,361 @@
+/*
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ routines to handle the stats files
+
+ the stats file is stored one per cache subdirectory to make this more
+ scalable
+ */
+
+#include "ccache.h"
+
+extern char *stats_file;
+extern char *cache_dir;
+
+#define STATS_VERSION 1
+
+#define FLAG_NOZERO 1 /* don't zero with the -z option */
+#define FLAG_ALWAYS 2 /* always show, even if zero */
+
+static struct {
+ enum stats stat;
+ char *message;
+ void (*fn)(unsigned );
+ unsigned flags;
+} stats_info[] = {
+ { STATS_CACHED, "cache hit ", NULL, FLAG_ALWAYS },
+ { STATS_TOCACHE, "cache miss ", NULL, FLAG_ALWAYS },
+ { STATS_LINK, "called for link ", NULL, 0 },
+ { STATS_MULTIPLE, "multiple source files ", NULL, 0 },
+ { STATS_STDOUT, "compiler produced stdout ", NULL, 0 },
+ { STATS_STATUS, "compile failed ", NULL, 0 },
+ { STATS_ERROR, "ccache internal error ", NULL, 0 },
+ { STATS_PREPROCESSOR, "preprocessor error ", NULL, 0 },
+ { STATS_COMPILER, "couldn't find the compiler ", NULL, 0 },
+ { STATS_MISSING, "cache file missing ", NULL, 0 },
+ { STATS_ARGS, "bad compiler arguments ", NULL, 0 },
+ { STATS_NOTC, "not a C/C++ file ", NULL, 0 },
+ { STATS_CONFTEST, "autoconf compile/link ", NULL, 0 },
+ { STATS_UNSUPPORTED, "unsupported compiler option ", NULL, 0 },
+ { STATS_OUTSTDOUT, "output to stdout ", NULL, 0 },
+ { STATS_DEVICE, "output to a non-regular file ", NULL, 0 },
+ { STATS_NOINPUT, "no input file ", NULL, 0 },
+ { STATS_ENVIRONMMENT, "error due to bad env variable ", NULL, 0 },
+ { STATS_NUMFILES, "files in cache ", NULL, FLAG_NOZERO|FLAG_ALWAYS },
+ { STATS_TOTALSIZE, "cache size ", display_size , FLAG_NOZERO|FLAG_ALWAYS },
+ { STATS_MAXFILES, "max files ", NULL, FLAG_NOZERO },
+ { STATS_MAXSIZE, "max cache size ", display_size, FLAG_NOZERO },
+ { STATS_NONE, NULL, NULL, 0 }
+};
+
+/* parse a stats file from a buffer - adding to the counters */
+static void parse_stats(unsigned counters[STATS_END], char *buf)
+{
+ int i;
+ char *p, *p2;
+
+ p = buf;
+ for (i=0;i<STATS_END;i++) {
+ counters[i] += strtol(p, &p2, 10);
+ if (!p2 || p2 == p) break;
+ p = p2;
+ }
+}
+
+/* write out a stats file */
+static void write_stats(int fd, unsigned counters[STATS_END])
+{
+ int i;
+ int len = 0;
+ char buf[1024];
+
+ for (i=0;i<STATS_END;i++) {
+ len += snprintf(buf+len, sizeof(buf)-(len+1), "%u ", counters[i]);
+ if (len >= (int)sizeof(buf)-1) fatal("stats too long?!");
+ }
+ len += snprintf(buf+len, sizeof(buf)-(len+1), "\n");
+ if (len >= (int)sizeof(buf)-1) fatal("stats too long?!");
+
+ lseek(fd, 0, SEEK_SET);
+ if (write(fd, buf, len) == -1) fatal("could not write stats");
+}
+
+
+/* fill in some default stats values */
+static void stats_default(unsigned counters[STATS_END])
+{
+ counters[STATS_MAXSIZE] += DEFAULT_MAXSIZE / 16;
+}
+
+/* read in the stats from one dir and add to the counters */
+static void stats_read_fd(int fd, unsigned counters[STATS_END])
+{
+ char buf[1024];
+ int len;
+ len = read(fd, buf, sizeof(buf)-1);
+ if (len <= 0) {
+ stats_default(counters);
+ return;
+ }
+ buf[len] = 0;
+ parse_stats(counters, buf);
+}
+
+/* update the stats counter for this compile */
+static void stats_update_size(enum stats stat, size_t size, size_t numfiles)
+{
+ int fd;
+ unsigned counters[STATS_END];
+ int need_cleanup = 0;
+
+ if (getenv("CCACHE_NOSTATS")) return;
+
+ if (!stats_file) {
+ if (!cache_dir) return;
+ x_asprintf(&stats_file, "%s/stats", cache_dir);
+ }
+
+ /* open safely to try to prevent symlink races */
+ fd = safe_open(stats_file);
+
+ /* still can't get it? don't bother ... */
+ if (fd == -1) return;
+
+ memset(counters, 0, sizeof(counters));
+
+ if (lock_fd(fd) != 0) return;
+
+ /* read in the old stats */
+ stats_read_fd(fd, counters);
+
+ /* update them */
+ counters[stat]++;
+
+ /* on a cache miss we up the file count and size */
+ if (stat == STATS_TOCACHE) {
+ counters[STATS_NUMFILES] += numfiles;
+ counters[STATS_TOTALSIZE] += size;
+ }
+
+ /* and write them out */
+ write_stats(fd, counters);
+ close(fd);
+
+ /* we might need to cleanup if the cache has now got too big */
+ if (counters[STATS_MAXFILES] != 0 &&
+ counters[STATS_NUMFILES] > counters[STATS_MAXFILES]) {
+ need_cleanup = 1;
+ }
+ if (counters[STATS_MAXSIZE] != 0 &&
+ counters[STATS_TOTALSIZE] > counters[STATS_MAXSIZE]) {
+ need_cleanup = 1;
+ }
+
+ if (need_cleanup) {
+ char *p = dirname(stats_file);
+ cleanup_dir(p, counters[STATS_MAXFILES], counters[STATS_MAXSIZE]);
+ free(p);
+ }
+}
+
+/* record a cache miss */
+void stats_tocache(size_t size, size_t numfiles)
+{
+ /* convert size to kilobytes */
+ size = size / 1024;
+
+ stats_update_size(STATS_TOCACHE, size, numfiles);
+}
+
+/* update a normal stat */
+void stats_update(enum stats stat)
+{
+ stats_update_size(stat, 0, 0);
+}
+
+/* read in the stats from one dir and add to the counters */
+void stats_read(const char *stats_file, unsigned counters[STATS_END])
+{
+ int fd;
+
+ fd = open(stats_file, O_RDONLY|O_BINARY);
+ if (fd == -1) {
+ stats_default(counters);
+ return;
+ }
+ lock_fd(fd);
+ stats_read_fd(fd, counters);
+ close(fd);
+}
+
+/* sum and display the total stats for all cache dirs */
+void stats_summary(void)
+{
+ int dir, i;
+ unsigned counters[STATS_END];
+
+ memset(counters, 0, sizeof(counters));
+
+ /* add up the stats in each directory */
+ for (dir=-1;dir<=0xF;dir++) {
+ char *fname;
+
+ if (dir == -1) {
+ x_asprintf(&fname, "%s/stats", cache_dir);
+ } else {
+ x_asprintf(&fname, "%s/%1x/stats", cache_dir, dir);
+ }
+
+ stats_read(fname, counters);
+ free(fname);
+
+ /* oh what a nasty hack ... */
+ if (dir == -1) {
+ counters[STATS_MAXSIZE] = 0;
+ }
+
+ }
+
+ printf("cache directory %s\n", cache_dir);
+
+ /* and display them */
+ for (i=0;stats_info[i].message;i++) {
+ enum stats stat = stats_info[i].stat;
+
+ if (counters[stat] == 0 &&
+ !(stats_info[i].flags & FLAG_ALWAYS)) {
+ continue;
+ }
+
+ printf("%s ", stats_info[i].message);
+ if (stats_info[i].fn) {
+ stats_info[i].fn(counters[stat]);
+ printf("\n");
+ } else {
+ printf("%8u\n", counters[stat]);
+ }
+ }
+}
+
+/* zero all the stats structures */
+void stats_zero(void)
+{
+ int dir, fd;
+ unsigned i;
+ char *fname;
+ unsigned counters[STATS_END];
+
+ x_asprintf(&fname, "%s/stats", cache_dir);
+ unlink(fname);
+ free(fname);
+
+ for (dir=0;dir<=0xF;dir++) {
+ x_asprintf(&fname, "%s/%1x/stats", cache_dir, dir);
+ fd = safe_open(fname);
+ if (fd == -1) {
+ free(fname);
+ continue;
+ }
+ memset(counters, 0, sizeof(counters));
+ lock_fd(fd);
+ stats_read_fd(fd, counters);
+ for (i=0;stats_info[i].message;i++) {
+ if (!(stats_info[i].flags & FLAG_NOZERO)) {
+ counters[stats_info[i].stat] = 0;
+ }
+ }
+ write_stats(fd, counters);
+ close(fd);
+ free(fname);
+ }
+}
+
+
+/* set the per directory limits */
+int stats_set_limits(long maxfiles, long maxsize)
+{
+ int dir;
+ unsigned counters[STATS_END];
+
+ 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;
+ int fd;
+
+ x_asprintf(&cdir, "%s/%1x", cache_dir, dir);
+ if (create_dir(cdir) != 0) {
+ return 1;
+ }
+ x_asprintf(&fname, "%s/stats", cdir);
+ free(cdir);
+
+ memset(counters, 0, sizeof(counters));
+ fd = safe_open(fname);
+ if (fd != -1) {
+ lock_fd(fd);
+ stats_read_fd(fd, counters);
+ if (maxfiles != -1) {
+ counters[STATS_MAXFILES] = maxfiles;
+ }
+ if (maxsize != -1) {
+ counters[STATS_MAXSIZE] = maxsize;
+ }
+ write_stats(fd, counters);
+ close(fd);
+ }
+ free(fname);
+ }
+
+ return 0;
+}
+
+/* set the per directory sizes */
+void stats_set_sizes(const char *dir, size_t num_files, size_t total_size)
+{
+ int fd;
+ unsigned counters[STATS_END];
+ char *stats_file;
+
+ create_dir(dir);
+ x_asprintf(&stats_file, "%s/stats", dir);
+
+ memset(counters, 0, sizeof(counters));
+
+ fd = safe_open(stats_file);
+ if (fd != -1) {
+ lock_fd(fd);
+ stats_read_fd(fd, counters);
+ counters[STATS_NUMFILES] = num_files;
+ counters[STATS_TOTALSIZE] = total_size;
+ write_stats(fd, counters);
+ close(fd);
+ }
+
+ free(stats_file);
+}