resetting manifest requested domain to floor
[platform/upstream/ccache.git] / stats.c
1 /*
2  * Copyright (C) 2002-2004 Andrew Tridgell
3  * Copyright (C) 2009-2011 Joel Rosdahl
4  *
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)
8  * any later version.
9  *
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
13  * more details.
14  *
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
18  */
19
20 /*
21  * Routines to handle the stats files The stats file is stored one per cache
22  * subdirectory to make this more scalable.
23  */
24
25 #include "ccache.h"
26 #include "hashutil.h"
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 extern char *stats_file;
37 extern char *cache_dir;
38 extern unsigned lock_staleness_limit;
39
40 static struct counters *counter_updates;
41
42 /* default maximum cache size */
43 #ifndef DEFAULT_MAXSIZE
44 #define DEFAULT_MAXSIZE (1024*1024)
45 #endif
46
47 #define FLAG_NOZERO 1 /* don't zero with the -z option */
48 #define FLAG_ALWAYS 2 /* always show, even if zero */
49
50 static void display_size(size_t v);
51
52 /* statistics fields in display order */
53 static struct {
54         enum stats stat;
55         char *message;
56         void (*fn)(size_t );
57         unsigned flags;
58 } stats_info[] = {
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 }
88 };
89
90 static void
91 display_size(size_t v)
92 {
93         char *s = format_size(v);
94         printf("%15s", s);
95         free(s);
96 }
97
98 /* parse a stats file from a buffer - adding to the counters */
99 static void
100 parse_stats(struct counters *counters, const char *buf)
101 {
102         size_t i = 0;
103         const char *p;
104         char *p2;
105         long val;
106
107         p = buf;
108         while (1) {
109                 val = strtol(p, &p2, 10);
110                 if (p2 == p) {
111                         break;
112                 }
113                 if (counters->size < i + 1) {
114                         counters_resize(counters, i + 1);
115                 }
116                 counters->data[i] += val;
117                 i++;
118                 p = p2;
119         }
120 }
121
122 /* write out a stats file */
123 void
124 stats_write(const char *path, struct counters *counters)
125 {
126         size_t i;
127         char *tmp_file;
128         FILE *f;
129
130         tmp_file = format("%s.tmp.%s", path, tmp_string());
131         f = fopen(tmp_file, "wb");
132         if (!f) {
133                 cc_log("Failed to open %s", tmp_file);
134                 goto end;
135         }
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);
139                 }
140         }
141         fclose(f);
142         x_rename(tmp_file, path);
143
144 end:
145         free(tmp_file);
146 }
147
148 /* fill in some default stats values */
149 static void
150 stats_default(struct counters *counters)
151 {
152         counters->data[STATS_MAXSIZE] += DEFAULT_MAXSIZE / 16;
153 }
154
155 static void
156 init_counter_updates(void)
157 {
158         if (!counter_updates) {
159                 counter_updates = counters_init(STATS_END);
160         }
161 }
162
163 /*
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.
166  */
167 void
168 stats_update_size(enum stats stat, size_t size, unsigned files)
169 {
170         init_counter_updates();
171         if (stat != STATS_NONE) {
172                 counter_updates->data[stat]++;
173         }
174         counter_updates->data[STATS_NUMFILES] += files;
175         counter_updates->data[STATS_TOTALSIZE] += size;
176 }
177
178 /* Read in the stats from one directory and add to the counters. */
179 void
180 stats_read(const char *sfile, struct counters *counters)
181 {
182         char *data = read_text_file(sfile);
183         if (data) {
184                 parse_stats(counters, data);
185         } else {
186                 stats_default(counters);
187         }
188         free(data);
189 }
190
191 /*
192  * Write counter updates in counter_updates to disk.
193  */
194 void
195 stats_flush(void)
196 {
197         struct counters *counters;
198         bool need_cleanup = false;
199         bool should_flush = false;
200         int i;
201         extern char *cache_logfile;
202
203         if (getenv("CCACHE_NOSTATS")) return;
204
205         init_counter_updates();
206
207         for (i = 0; i < STATS_END; ++i) {
208                 if (counter_updates->data[i] > 0) {
209                         should_flush = true;
210                         break;
211                 }
212         }
213         if (!should_flush) return;
214
215         if (!stats_file) {
216                 char *stats_dir;
217
218                 /*
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.
221                  */
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);
226                 free(stats_dir);
227         }
228
229         if (!lockfile_acquire(stats_file, lock_staleness_limit)) {
230                 return;
231         }
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];
236         }
237         stats_write(stats_file, counters);
238         lockfile_release(stats_file);
239
240         if (cache_logfile) {
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);
245                         }
246                 }
247         }
248
249         if (counters->data[STATS_MAXFILES] != 0 &&
250             counters->data[STATS_NUMFILES] > counters->data[STATS_MAXFILES]) {
251                 need_cleanup = true;
252         }
253         if (counters->data[STATS_MAXSIZE] != 0 &&
254             counters->data[STATS_TOTALSIZE] > counters->data[STATS_MAXSIZE]) {
255                 need_cleanup = true;
256         }
257
258         if (need_cleanup) {
259                 char *p = dirname(stats_file);
260                 cleanup_dir(p,
261                             counters->data[STATS_MAXFILES],
262                             counters->data[STATS_MAXSIZE]);
263                 free(p);
264         }
265 }
266
267 /* update a normal stat */
268 void
269 stats_update(enum stats stat)
270 {
271         stats_update_size(stat, 0, 0);
272 }
273
274 /* Get the pending update of a counter value. */
275 unsigned
276 stats_get_pending(enum stats stat)
277 {
278         init_counter_updates();
279         return counter_updates->data[stat];
280 }
281
282 /* sum and display the total stats for all cache dirs */
283 void
284 stats_summary(void)
285 {
286         int dir, i;
287         struct counters *counters = counters_init(STATS_END);
288
289         /* add up the stats in each directory */
290         for (dir = -1; dir <= 0xF; dir++) {
291                 char *fname;
292
293                 if (dir == -1) {
294                         fname = format("%s/stats", cache_dir);
295                 } else {
296                         fname = format("%s/%1x/stats", cache_dir, dir);
297                 }
298
299                 stats_read(fname, counters);
300                 free(fname);
301
302                 /* oh what a nasty hack ... */
303                 if (dir == -1) {
304                         counters->data[STATS_MAXSIZE] = 0;
305                 }
306         }
307
308         printf("cache directory                     %s\n", cache_dir);
309
310         /* and display them */
311         for (i = 0; stats_info[i].message; i++) {
312                 enum stats stat = stats_info[i].stat;
313
314                 if (counters->data[stat] == 0 && !(stats_info[i].flags & FLAG_ALWAYS)) {
315                         continue;
316                 }
317
318                 printf("%s ", stats_info[i].message);
319                 if (stats_info[i].fn) {
320                         stats_info[i].fn(counters->data[stat]);
321                         printf("\n");
322                 } else {
323                         printf("%8u\n", counters->data[stat]);
324                 }
325         }
326
327         counters_free(counters);
328 }
329
330 /* zero all the stats structures */
331 void
332 stats_zero(void)
333 {
334         int dir;
335         unsigned i;
336         char *fname;
337
338         fname = format("%s/stats", cache_dir);
339         x_unlink(fname);
340         free(fname);
341
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;
350                                 }
351                         }
352                         stats_write(fname, counters);
353                         lockfile_release(fname);
354                 }
355                 counters_free(counters);
356                 free(fname);
357         }
358 }
359
360 /* Get the per directory limits */
361 void
362 stats_get_limits(const char *dir, unsigned *maxfiles, unsigned *maxsize)
363 {
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];
369         free(sname);
370         counters_free(counters);
371 }
372
373 /* set the per directory limits */
374 int
375 stats_set_limits(long maxfiles, long maxsize)
376 {
377         int dir;
378
379         if (maxfiles != -1) {
380                 maxfiles /= 16;
381         }
382         if (maxsize != -1) {
383                 maxsize /= 16;
384         }
385
386         if (create_dir(cache_dir) != 0) {
387                 return 1;
388         }
389
390         /* set the limits in each directory */
391         for (dir = 0; dir <= 0xF; dir++) {
392                 char *fname, *cdir;
393
394                 cdir = format("%s/%1x", cache_dir, dir);
395                 if (create_dir(cdir) != 0) {
396                         free(cdir);
397                         return 1;
398                 }
399                 fname = format("%s/stats", cdir);
400                 free(cdir);
401
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;
407                         }
408                         if (maxsize != -1) {
409                                 counters->data[STATS_MAXSIZE] = maxsize;
410                         }
411                         stats_write(fname, counters);
412                         lockfile_release(fname);
413                         counters_free(counters);
414                 }
415                 free(fname);
416         }
417
418         return 0;
419 }
420
421 /* set the per directory sizes */
422 void
423 stats_set_sizes(const char *dir, size_t num_files, size_t total_size)
424 {
425         struct counters *counters = counters_init(STATS_END);
426         char *statsfile;
427
428         create_dir(dir);
429         statsfile = format("%s/stats", dir);
430
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);
437         }
438         free(statsfile);
439         counters_free(counters);
440 }