1899
|
1 /*
|
|
2 Copyright (C) Andrew Tridgell 2002
|
|
3
|
|
4 This program is free software; you can redistribute it and/or modify
|
|
5 it under the terms of the GNU General Public License as published by
|
|
6 the Free Software Foundation; either version 2 of the License, or
|
|
7 (at your option) any later version.
|
|
8
|
|
9 This program is distributed in the hope that it will be useful,
|
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 GNU General Public License for more details.
|
|
13
|
|
14 You should have received a copy of the GNU General Public License
|
|
15 along with this program; if not, write to the Free Software
|
|
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
17 */
|
|
18 /*
|
|
19 routines to handle the stats files
|
|
20
|
|
21 the stats file is stored one per cache subdirectory to make this more
|
|
22 scalable
|
|
23 */
|
|
24
|
|
25 #include "ccache.h"
|
|
26
|
|
27 extern char *stats_file;
|
|
28 extern char *cache_dir;
|
|
29
|
|
30 #define STATS_VERSION 1
|
|
31
|
|
32 #define FLAG_NOZERO 1 /* don't zero with the -z option */
|
|
33 #define FLAG_ALWAYS 2 /* always show, even if zero */
|
|
34
|
|
35 static struct {
|
|
36 enum stats stat;
|
|
37 char *message;
|
|
38 void (*fn)(unsigned );
|
|
39 unsigned flags;
|
|
40 } stats_info[] = {
|
|
41 { STATS_CACHED, "cache hit ", NULL, FLAG_ALWAYS },
|
|
42 { STATS_TOCACHE, "cache miss ", NULL, FLAG_ALWAYS },
|
|
43 { STATS_LINK, "called for link ", NULL, 0 },
|
|
44 { STATS_MULTIPLE, "multiple source files ", NULL, 0 },
|
|
45 { STATS_STDOUT, "compiler produced stdout ", NULL, 0 },
|
|
46 { STATS_STATUS, "compile failed ", NULL, 0 },
|
|
47 { STATS_ERROR, "ccache internal error ", NULL, 0 },
|
|
48 { STATS_PREPROCESSOR, "preprocessor error ", NULL, 0 },
|
|
49 { STATS_COMPILER, "couldn't find the compiler ", NULL, 0 },
|
|
50 { STATS_MISSING, "cache file missing ", NULL, 0 },
|
|
51 { STATS_ARGS, "bad compiler arguments ", NULL, 0 },
|
|
52 { STATS_NOTC, "not a C/C++ file ", NULL, 0 },
|
|
53 { STATS_CONFTEST, "autoconf compile/link ", NULL, 0 },
|
|
54 { STATS_UNSUPPORTED, "unsupported compiler option ", NULL, 0 },
|
|
55 { STATS_OUTSTDOUT, "output to stdout ", NULL, 0 },
|
|
56 { STATS_DEVICE, "output to a non-regular file ", NULL, 0 },
|
|
57 { STATS_NOINPUT, "no input file ", NULL, 0 },
|
|
58 { STATS_ENVIRONMMENT, "error due to bad env variable ", NULL, 0 },
|
|
59 { STATS_NUMFILES, "files in cache ", NULL, FLAG_NOZERO|FLAG_ALWAYS },
|
|
60 { STATS_TOTALSIZE, "cache size ", display_size , FLAG_NOZERO|FLAG_ALWAYS },
|
|
61 { STATS_MAXFILES, "max files ", NULL, FLAG_NOZERO },
|
|
62 { STATS_MAXSIZE, "max cache size ", display_size, FLAG_NOZERO },
|
|
63 { STATS_NONE, NULL, NULL, 0 }
|
|
64 };
|
|
65
|
|
66 /* parse a stats file from a buffer - adding to the counters */
|
|
67 static void parse_stats(unsigned counters[STATS_END], char *buf)
|
|
68 {
|
|
69 int i;
|
|
70 char *p, *p2;
|
|
71
|
|
72 p = buf;
|
|
73 for (i=0;i<STATS_END;i++) {
|
|
74 counters[i] += strtol(p, &p2, 10);
|
|
75 if (!p2 || p2 == p) break;
|
|
76 p = p2;
|
|
77 }
|
|
78 }
|
|
79
|
|
80 /* write out a stats file */
|
|
81 static void write_stats(int fd, unsigned counters[STATS_END])
|
|
82 {
|
|
83 int i;
|
|
84 int len = 0;
|
|
85 char buf[1024];
|
|
86
|
|
87 for (i=0;i<STATS_END;i++) {
|
|
88 len += snprintf(buf+len, sizeof(buf)-(len+1), "%u ", counters[i]);
|
|
89 if (len >= (int)sizeof(buf)-1) fatal("stats too long?!");
|
|
90 }
|
|
91 len += snprintf(buf+len, sizeof(buf)-(len+1), "\n");
|
|
92 if (len >= (int)sizeof(buf)-1) fatal("stats too long?!");
|
|
93
|
|
94 lseek(fd, 0, SEEK_SET);
|
|
95 if (write(fd, buf, len) == -1) fatal("could not write stats");
|
|
96 }
|
|
97
|
|
98
|
|
99 /* fill in some default stats values */
|
|
100 static void stats_default(unsigned counters[STATS_END])
|
|
101 {
|
|
102 counters[STATS_MAXSIZE] += DEFAULT_MAXSIZE / 16;
|
|
103 }
|
|
104
|
|
105 /* read in the stats from one dir and add to the counters */
|
|
106 static void stats_read_fd(int fd, unsigned counters[STATS_END])
|
|
107 {
|
|
108 char buf[1024];
|
|
109 int len;
|
|
110 len = read(fd, buf, sizeof(buf)-1);
|
|
111 if (len <= 0) {
|
|
112 stats_default(counters);
|
|
113 return;
|
|
114 }
|
|
115 buf[len] = 0;
|
|
116 parse_stats(counters, buf);
|
|
117 }
|
|
118
|
|
119 /* update the stats counter for this compile */
|
|
120 static void stats_update_size(enum stats stat, size_t size, size_t numfiles)
|
|
121 {
|
|
122 int fd;
|
|
123 unsigned counters[STATS_END];
|
|
124 int need_cleanup = 0;
|
|
125
|
|
126 if (getenv("CCACHE_NOSTATS")) return;
|
|
127
|
|
128 if (!stats_file) {
|
|
129 if (!cache_dir) return;
|
|
130 x_asprintf(&stats_file, "%s/stats", cache_dir);
|
|
131 }
|
|
132
|
|
133 /* open safely to try to prevent symlink races */
|
|
134 fd = safe_open(stats_file);
|
|
135
|
|
136 /* still can't get it? don't bother ... */
|
|
137 if (fd == -1) return;
|
|
138
|
|
139 memset(counters, 0, sizeof(counters));
|
|
140
|
|
141 if (lock_fd(fd) != 0) return;
|
|
142
|
|
143 /* read in the old stats */
|
|
144 stats_read_fd(fd, counters);
|
|
145
|
|
146 /* update them */
|
|
147 counters[stat]++;
|
|
148
|
|
149 /* on a cache miss we up the file count and size */
|
|
150 if (stat == STATS_TOCACHE) {
|
|
151 counters[STATS_NUMFILES] += numfiles;
|
|
152 counters[STATS_TOTALSIZE] += size;
|
|
153 }
|
|
154
|
|
155 /* and write them out */
|
|
156 write_stats(fd, counters);
|
|
157 close(fd);
|
|
158
|
|
159 /* we might need to cleanup if the cache has now got too big */
|
|
160 if (counters[STATS_MAXFILES] != 0 &&
|
|
161 counters[STATS_NUMFILES] > counters[STATS_MAXFILES]) {
|
|
162 need_cleanup = 1;
|
|
163 }
|
|
164 if (counters[STATS_MAXSIZE] != 0 &&
|
|
165 counters[STATS_TOTALSIZE] > counters[STATS_MAXSIZE]) {
|
|
166 need_cleanup = 1;
|
|
167 }
|
|
168
|
|
169 if (need_cleanup) {
|
|
170 char *p = dirname(stats_file);
|
|
171 cleanup_dir(p, counters[STATS_MAXFILES], counters[STATS_MAXSIZE],
|
|
172 numfiles);
|
|
173 free(p);
|
|
174 }
|
|
175 }
|
|
176
|
|
177 /* record a cache miss */
|
|
178 void stats_tocache(size_t size, size_t numfiles)
|
|
179 {
|
|
180 /* convert size to kilobytes */
|
|
181 size = size / 1024;
|
|
182
|
|
183 stats_update_size(STATS_TOCACHE, size, numfiles);
|
|
184 }
|
|
185
|
|
186 /* update a normal stat */
|
|
187 void stats_update(enum stats stat)
|
|
188 {
|
|
189 stats_update_size(stat, 0, 0);
|
|
190 }
|
|
191
|
|
192 /* read in the stats from one dir and add to the counters */
|
|
193 void stats_read(const char *stats_file, unsigned counters[STATS_END])
|
|
194 {
|
|
195 int fd;
|
|
196
|
|
197 fd = open(stats_file, O_RDONLY|O_BINARY);
|
|
198 if (fd == -1) {
|
|
199 stats_default(counters);
|
|
200 return;
|
|
201 }
|
|
202 lock_fd(fd);
|
|
203 stats_read_fd(fd, counters);
|
|
204 close(fd);
|
|
205 }
|
|
206
|
|
207 /* sum and display the total stats for all cache dirs */
|
|
208 void stats_summary(void)
|
|
209 {
|
|
210 int dir, i;
|
|
211 unsigned counters[STATS_END];
|
|
212
|
|
213 memset(counters, 0, sizeof(counters));
|
|
214
|
|
215 /* add up the stats in each directory */
|
|
216 for (dir=-1;dir<=0xF;dir++) {
|
|
217 char *fname;
|
|
218
|
|
219 if (dir == -1) {
|
|
220 x_asprintf(&fname, "%s/stats", cache_dir);
|
|
221 } else {
|
|
222 x_asprintf(&fname, "%s/%1x/stats", cache_dir, dir);
|
|
223 }
|
|
224
|
|
225 stats_read(fname, counters);
|
|
226 free(fname);
|
|
227
|
|
228 /* oh what a nasty hack ... */
|
|
229 if (dir == -1) {
|
|
230 counters[STATS_MAXSIZE] = 0;
|
|
231 }
|
|
232
|
|
233 }
|
|
234
|
|
235 printf("cache directory %s\n", cache_dir);
|
|
236
|
|
237 /* and display them */
|
|
238 for (i=0;stats_info[i].message;i++) {
|
|
239 enum stats stat = stats_info[i].stat;
|
|
240
|
|
241 if (counters[stat] == 0 &&
|
|
242 !(stats_info[i].flags & FLAG_ALWAYS)) {
|
|
243 continue;
|
|
244 }
|
|
245
|
|
246 printf("%s ", stats_info[i].message);
|
|
247 if (stats_info[i].fn) {
|
|
248 stats_info[i].fn(counters[stat]);
|
|
249 printf("\n");
|
|
250 } else {
|
|
251 printf("%8u\n", counters[stat]);
|
|
252 }
|
|
253 }
|
|
254 }
|
|
255
|
|
256 /* zero all the stats structures */
|
|
257 void stats_zero(void)
|
|
258 {
|
|
259 int dir, fd;
|
|
260 unsigned i;
|
|
261 char *fname;
|
|
262 unsigned counters[STATS_END];
|
|
263
|
|
264 x_asprintf(&fname, "%s/stats", cache_dir);
|
|
265 unlink(fname);
|
|
266 free(fname);
|
|
267
|
|
268 for (dir=0;dir<=0xF;dir++) {
|
|
269 x_asprintf(&fname, "%s/%1x/stats", cache_dir, dir);
|
|
270 fd = safe_open(fname);
|
|
271 if (fd == -1) {
|
|
272 free(fname);
|
|
273 continue;
|
|
274 }
|
|
275 memset(counters, 0, sizeof(counters));
|
|
276 lock_fd(fd);
|
|
277 stats_read_fd(fd, counters);
|
|
278 for (i=0;stats_info[i].message;i++) {
|
|
279 if (!(stats_info[i].flags & FLAG_NOZERO)) {
|
|
280 counters[stats_info[i].stat] = 0;
|
|
281 }
|
|
282 }
|
|
283 write_stats(fd, counters);
|
|
284 close(fd);
|
|
285 free(fname);
|
|
286 }
|
|
287 }
|
|
288
|
|
289
|
|
290 /* set the per directory limits */
|
|
291 int stats_set_limits(long maxfiles, long maxsize)
|
|
292 {
|
|
293 int dir;
|
|
294 unsigned counters[STATS_END];
|
|
295
|
|
296 if (maxfiles != -1) {
|
|
297 maxfiles /= 16;
|
|
298 }
|
|
299 if (maxsize != -1) {
|
|
300 maxsize /= 16;
|
|
301 }
|
|
302
|
|
303 if (create_dir(cache_dir) != 0) {
|
|
304 return 1;
|
|
305 }
|
|
306
|
|
307 /* set the limits in each directory */
|
|
308 for (dir=0;dir<=0xF;dir++) {
|
|
309 char *fname, *cdir;
|
|
310 int fd;
|
|
311
|
|
312 x_asprintf(&cdir, "%s/%1x", cache_dir, dir);
|
|
313 if (create_dir(cdir) != 0) {
|
|
314 return 1;
|
|
315 }
|
|
316 x_asprintf(&fname, "%s/stats", cdir);
|
|
317 free(cdir);
|
|
318
|
|
319 memset(counters, 0, sizeof(counters));
|
|
320 fd = safe_open(fname);
|
|
321 if (fd != -1) {
|
|
322 lock_fd(fd);
|
|
323 stats_read_fd(fd, counters);
|
|
324 if (maxfiles != -1) {
|
|
325 counters[STATS_MAXFILES] = maxfiles;
|
|
326 }
|
|
327 if (maxsize != -1) {
|
|
328 counters[STATS_MAXSIZE] = maxsize;
|
|
329 }
|
|
330 write_stats(fd, counters);
|
|
331 close(fd);
|
|
332 }
|
|
333 free(fname);
|
|
334 }
|
|
335
|
|
336 return 0;
|
|
337 }
|
|
338
|
|
339 /* set the per directory sizes */
|
|
340 void stats_set_sizes(const char *dir, size_t num_files, size_t total_size)
|
|
341 {
|
|
342 int fd;
|
|
343 unsigned counters[STATS_END];
|
|
344 char *stats_file;
|
|
345
|
|
346 create_dir(dir);
|
|
347 x_asprintf(&stats_file, "%s/stats", dir);
|
|
348
|
|
349 memset(counters, 0, sizeof(counters));
|
|
350
|
|
351 fd = safe_open(stats_file);
|
|
352 if (fd != -1) {
|
|
353 lock_fd(fd);
|
|
354 stats_read_fd(fd, counters);
|
|
355 counters[STATS_NUMFILES] = num_files;
|
|
356 counters[STATS_TOTALSIZE] = total_size;
|
|
357 write_stats(fd, counters);
|
|
358 close(fd);
|
|
359 }
|
|
360
|
|
361 free(stats_file);
|
|
362 }
|