2 * Copyright (C) 2002-2004 Andrew Tridgell
3 * Copyright (C) 2009-2011 Joel Rosdahl
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 3 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * Routines to handle the stats files The stats file is stored one per cache
22 * subdirectory to make this more scalable.
28 #include <sys/types.h>
36 extern char *stats_file;
37 extern char *cache_dir;
38 extern unsigned lock_staleness_limit;
40 static struct counters *counter_updates;
42 /* default maximum cache size */
43 #ifndef DEFAULT_MAXSIZE
44 #define DEFAULT_MAXSIZE (1024*1024)
47 #define FLAG_NOZERO 1 /* don't zero with the -z option */
48 #define FLAG_ALWAYS 2 /* always show, even if zero */
50 static void display_size(size_t v);
52 /* statistics fields in display order */
59 { STATS_CACHEHIT_DIR, "cache hit (direct) ", NULL, FLAG_ALWAYS },
60 { STATS_CACHEHIT_CPP, "cache hit (preprocessed) ", NULL, FLAG_ALWAYS },
61 { STATS_TOCACHE, "cache miss ", NULL, FLAG_ALWAYS },
62 { STATS_LINK, "called for link ", NULL, 0 },
63 { STATS_PREPROCESSING, "called for preprocessing ", NULL, 0 },
64 { STATS_MULTIPLE, "multiple source files ", NULL, 0 },
65 { STATS_STDOUT, "compiler produced stdout ", NULL, 0 },
66 { STATS_NOOUTPUT, "compiler produced no output ", NULL, 0 },
67 { STATS_EMPTYOUTPUT, "compiler produced empty output ", NULL, 0 },
68 { STATS_STATUS, "compile failed ", NULL, 0 },
69 { STATS_ERROR, "ccache internal error ", NULL, 0 },
70 { STATS_PREPROCESSOR, "preprocessor error ", NULL, 0 },
71 { STATS_CANTUSEPCH, "can't use precompiled header ", NULL, 0 },
72 { STATS_COMPILER, "couldn't find the compiler ", NULL, 0 },
73 { STATS_MISSING, "cache file missing ", NULL, 0 },
74 { STATS_ARGS, "bad compiler arguments ", NULL, 0 },
75 { STATS_SOURCELANG, "unsupported source language ", NULL, 0 },
76 { STATS_COMPCHECK, "compiler check failed ", NULL, 0 },
77 { STATS_CONFTEST, "autoconf compile/link ", NULL, 0 },
78 { STATS_UNSUPPORTED, "unsupported compiler option ", NULL, 0 },
79 { STATS_OUTSTDOUT, "output to stdout ", NULL, 0 },
80 { STATS_DEVICE, "output to a non-regular file ", NULL, 0 },
81 { STATS_NOINPUT, "no input file ", NULL, 0 },
82 { STATS_BADEXTRAFILE, "error hashing extra file ", NULL, 0 },
83 { STATS_NUMFILES, "files in cache ", NULL, FLAG_NOZERO|FLAG_ALWAYS },
84 { STATS_TOTALSIZE, "cache size ", display_size , FLAG_NOZERO|FLAG_ALWAYS },
85 { STATS_MAXFILES, "max files ", NULL, FLAG_NOZERO },
86 { STATS_MAXSIZE, "max cache size ", display_size, FLAG_NOZERO },
87 { STATS_NONE, NULL, NULL, 0 }
91 display_size(size_t v)
93 char *s = format_size(v);
98 /* parse a stats file from a buffer - adding to the counters */
100 parse_stats(struct counters *counters, const char *buf)
109 val = strtol(p, &p2, 10);
113 if (counters->size < i + 1) {
114 counters_resize(counters, i + 1);
116 counters->data[i] += val;
122 /* write out a stats file */
124 stats_write(const char *path, struct counters *counters)
130 tmp_file = format("%s.tmp.%s", path, tmp_string());
131 f = fopen(tmp_file, "wb");
133 cc_log("Failed to open %s", tmp_file);
136 for (i = 0; i < counters->size; i++) {
137 if (fprintf(f, "%u\n", counters->data[i]) < 0) {
138 fatal("Failed to write to %s", tmp_file);
142 x_rename(tmp_file, path);
148 /* fill in some default stats values */
150 stats_default(struct counters *counters)
152 counters->data[STATS_MAXSIZE] += DEFAULT_MAXSIZE / 16;
156 init_counter_updates(void)
158 if (!counter_updates) {
159 counter_updates = counters_init(STATS_END);
164 * Update a statistics counter (unless it's STATS_NONE) and also record that a
165 * number of bytes and files have been added to the cache. Size is in KiB.
168 stats_update_size(enum stats stat, size_t size, unsigned files)
170 init_counter_updates();
171 if (stat != STATS_NONE) {
172 counter_updates->data[stat]++;
174 counter_updates->data[STATS_NUMFILES] += files;
175 counter_updates->data[STATS_TOTALSIZE] += size;
178 /* Read in the stats from one directory and add to the counters. */
180 stats_read(const char *sfile, struct counters *counters)
182 char *data = read_text_file(sfile);
184 parse_stats(counters, data);
186 stats_default(counters);
192 * Write counter updates in counter_updates to disk.
197 struct counters *counters;
198 bool need_cleanup = false;
199 bool should_flush = false;
201 extern char *cache_logfile;
203 if (getenv("CCACHE_NOSTATS")) return;
205 init_counter_updates();
207 for (i = 0; i < STATS_END; ++i) {
208 if (counter_updates->data[i] > 0) {
213 if (!should_flush) return;
219 * A NULL stats_file means that we didn't get past calculate_object_hash(),
220 * so we just choose one of stats files in the 16 subdirectories.
222 if (!cache_dir) return;
223 stats_dir = format("%s/%x", cache_dir, hash_from_int(getpid()) % 16);
224 stats_file = format("%s/stats", stats_dir);
225 create_dir(stats_dir);
229 if (!lockfile_acquire(stats_file, lock_staleness_limit)) {
232 counters = counters_init(STATS_END);
233 stats_read(stats_file, counters);
234 for (i = 0; i < STATS_END; ++i) {
235 counters->data[i] += counter_updates->data[i];
237 stats_write(stats_file, counters);
238 lockfile_release(stats_file);
241 for (i = 0; i < STATS_END; ++i) {
242 if (counter_updates->data[stats_info[i].stat] != 0
243 && !(stats_info[i].flags & FLAG_NOZERO)) {
244 cc_log("Result: %s", stats_info[i].message);
249 if (counters->data[STATS_MAXFILES] != 0 &&
250 counters->data[STATS_NUMFILES] > counters->data[STATS_MAXFILES]) {
253 if (counters->data[STATS_MAXSIZE] != 0 &&
254 counters->data[STATS_TOTALSIZE] > counters->data[STATS_MAXSIZE]) {
259 char *p = dirname(stats_file);
261 counters->data[STATS_MAXFILES],
262 counters->data[STATS_MAXSIZE]);
267 /* update a normal stat */
269 stats_update(enum stats stat)
271 stats_update_size(stat, 0, 0);
274 /* Get the pending update of a counter value. */
276 stats_get_pending(enum stats stat)
278 init_counter_updates();
279 return counter_updates->data[stat];
282 /* sum and display the total stats for all cache dirs */
287 struct counters *counters = counters_init(STATS_END);
289 /* add up the stats in each directory */
290 for (dir = -1; dir <= 0xF; dir++) {
294 fname = format("%s/stats", cache_dir);
296 fname = format("%s/%1x/stats", cache_dir, dir);
299 stats_read(fname, counters);
302 /* oh what a nasty hack ... */
304 counters->data[STATS_MAXSIZE] = 0;
308 printf("cache directory %s\n", cache_dir);
310 /* and display them */
311 for (i = 0; stats_info[i].message; i++) {
312 enum stats stat = stats_info[i].stat;
314 if (counters->data[stat] == 0 && !(stats_info[i].flags & FLAG_ALWAYS)) {
318 printf("%s ", stats_info[i].message);
319 if (stats_info[i].fn) {
320 stats_info[i].fn(counters->data[stat]);
323 printf("%8u\n", counters->data[stat]);
327 counters_free(counters);
330 /* zero all the stats structures */
338 fname = format("%s/stats", cache_dir);
342 for (dir = 0; dir <= 0xF; dir++) {
343 struct counters *counters = counters_init(STATS_END);
344 fname = format("%s/%1x/stats", cache_dir, dir);
345 if (lockfile_acquire(fname, lock_staleness_limit)) {
346 stats_read(fname, counters);
347 for (i = 0; stats_info[i].message; i++) {
348 if (!(stats_info[i].flags & FLAG_NOZERO)) {
349 counters->data[stats_info[i].stat] = 0;
352 stats_write(fname, counters);
353 lockfile_release(fname);
355 counters_free(counters);
360 /* Get the per directory limits */
362 stats_get_limits(const char *dir, unsigned *maxfiles, unsigned *maxsize)
364 struct counters *counters = counters_init(STATS_END);
365 char *sname = format("%s/stats", dir);
366 stats_read(sname, counters);
367 *maxfiles = counters->data[STATS_MAXFILES];
368 *maxsize = counters->data[STATS_MAXSIZE];
370 counters_free(counters);
373 /* set the per directory limits */
375 stats_set_limits(long maxfiles, long maxsize)
379 if (maxfiles != -1) {
386 if (create_dir(cache_dir) != 0) {
390 /* set the limits in each directory */
391 for (dir = 0; dir <= 0xF; dir++) {
394 cdir = format("%s/%1x", cache_dir, dir);
395 if (create_dir(cdir) != 0) {
399 fname = format("%s/stats", cdir);
402 if (lockfile_acquire(fname, lock_staleness_limit)) {
403 struct counters *counters = counters_init(STATS_END);
404 stats_read(fname, counters);
405 if (maxfiles != -1) {
406 counters->data[STATS_MAXFILES] = maxfiles;
409 counters->data[STATS_MAXSIZE] = maxsize;
411 stats_write(fname, counters);
412 lockfile_release(fname);
413 counters_free(counters);
421 /* set the per directory sizes */
423 stats_set_sizes(const char *dir, size_t num_files, size_t total_size)
425 struct counters *counters = counters_init(STATS_END);
429 statsfile = format("%s/stats", dir);
431 if (lockfile_acquire(statsfile, lock_staleness_limit)) {
432 stats_read(statsfile, counters);
433 counters->data[STATS_NUMFILES] = num_files;
434 counters->data[STATS_TOTALSIZE] = total_size;
435 stats_write(statsfile, counters);
436 lockfile_release(statsfile);
439 counters_free(counters);