From: Andrew Tridgell Date: Tue, 26 Mar 2002 14:46:43 +0000 (+0100) Subject: first version of C compilercache X-Git-Tag: v1.0~51 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f42859a15c39f0efd28382e7e7031895247cc085;p=thirdparty%2Fccache.git first version of C compilercache --- f42859a15c39f0efd28382e7e7031895247cc085 diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..565d5dbe7 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CFLAGS=-W -Wall -g +CC=gcc + +OBJS= ccache.o mdfour.o hash.o execute.o util.o args.o +HEADERS = ccache.h mdfour.h + +ccache: $(OBJS) $(HEADERS) + $(CC) -o $@ $(OBJS) + +clean: + /bin/rm -f $(OBJS) *~ ccache diff --git a/args.c b/args.c new file mode 100644 index 000000000..bd9ce21a3 --- /dev/null +++ b/args.c @@ -0,0 +1,21 @@ +#include "ccache.h" + +ARGS *args_init(void) +{ + ARGS *args; + args = malloc(sizeof(ARGS)); + args->argc = 0; + args->argv = malloc(sizeof(char *)); + args->argv[0] = NULL; + return args; +} + + +void args_add(ARGS *args, const char *s) +{ + args->argv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); + args->argv[args->argc] = strdup(s); + args->argc++; + args->argv[args->argc] = NULL; +} + diff --git a/ccache.c b/ccache.c new file mode 100644 index 000000000..84cd2a434 --- /dev/null +++ b/ccache.c @@ -0,0 +1,373 @@ +/* + a re-implementation of the compilercache scripts in C + Copyright tridge@samba.org 2002 + + The idea is based on the shell-script compilercache by Erik Thiele +*/ + +#include "ccache.h" + +static char *cache_dir = CACHE_BASEDIR; + + +static ARGS *stripped_args; +static ARGS *orig_args; +static char *output_file; +static char *hashname; +static int found_debug; + +static void failed(void) +{ + execv(orig_args->argv[0], orig_args->argv); + cc_log("execv returned (%s)!\n", strerror(errno)); + exit(1); +} + +/* run the real compiler and put the result in cache */ +static void to_cache(ARGS *args) +{ + char *path_stdout, *path_status, *path_stderr; + struct stat st; + + x_asprintf(&path_status, "%s.status", hashname); + x_asprintf(&path_stderr, "%s.stderr", hashname); + x_asprintf(&path_stdout, "%s.stdout", hashname); + + args_add(args, "-o"); + args_add(args, hashname); + + execute(args->argv, path_stdout, path_stderr, path_status); + + args->argc -= 2; + + if (stat(path_stdout, &st) != 0 || st.st_size != 0) { + cc_log("compiler produced stdout!\n"); + unlink(path_stdout); + unlink(path_stderr); + unlink(path_status); + unlink(hashname); + failed(); + } + + unlink(path_stdout); + cc_log("Placed %s into cache\n", output_file); +} + +static void stabs_hash(const char *fname) +{ + FILE *f; + char *s; + char line[1024]; + + line[sizeof(line)-2] = 0; + + f = fopen(fname, "r"); + if (!f) { + cc_log("Failed to open preprocessor output\n"); + failed(); + } + + while ((s = fgets(line, sizeof(line), f))) { + if (line[sizeof(line)-2]) { + cc_log("line too long in preprocessor output!\n"); + failed(); + } + + /* ignore debugging output */ + if (line[0] == '#' && line[1] == ' ' && isdigit(line[2])) { + continue; + } + + hash_string(s); + } + + fclose(f); +} + + +/* find the hash for a command. The hash includes all argument lists, + plus the output from running the compiler with -E */ +static void find_hash(ARGS *args) +{ + int i; + char *path_stdout, *path_stderr, *path_status; + + hash_start(); + + /* first the arguments */ + for (i=0;iargc;i++) { + hash_string(args->argv[i]); + } + + /* now the run */ + x_asprintf(&path_stdout, "%s/tmp.stdout.%d", cache_dir, getpid()); + x_asprintf(&path_stderr, "%s/tmp.stderr.%d", cache_dir, getpid()); + x_asprintf(&path_status, "%s/tmp.status.%d", cache_dir, getpid()); + + args_add(args, "-E"); + execute(args->argv, path_stdout, path_stderr, path_status); + args->argc--; + + if (found_debug) { + hash_file(path_stdout); + } else { + stabs_hash(path_stdout); + } + hash_file(path_stderr); + hash_file(path_status); + + unlink(path_stdout); + unlink(path_stderr); + unlink(path_status); + free(path_stdout); + free(path_stderr); + free(path_status); + + x_asprintf(&hashname, "%s/%s", cache_dir, hash_result()); +} + + +/* + try to return the compile result from cache. If we can return from + cache then this function exits with the correct status code, + otherwise it returns */ +static void from_cache(int first) +{ + int fd_status, fd_stderr; + char *s; + int ret, status; + + x_asprintf(&s, "%s.status", hashname); + fd_status = open(s, O_RDONLY); + free(s); + if (fd_status == -1) { + /* its not cached */ + return; + } + if (read(fd_status, &status, sizeof(status)) != sizeof(status)) { + cc_log("status file is too short\n"); + close(fd_status); + return; + } + close(fd_status); + + x_asprintf(&s, "%s.stderr", hashname); + fd_stderr = open(s, O_RDONLY); + free(s); + if (fd_stderr == -1) { + cc_log("stderr file not found\n"); + return; + } + + unlink(output_file); + ret = link(hashname, output_file); + if (ret == -1 && errno != ENOENT) { + /* copy it instead */ + cc_log("copy not implemented\n"); + failed(); + } + + /* send the stderr */ + copy_fd(fd_stderr, 2); + close(fd_stderr); + + /* and exit with the right status code */ + if (first) { + cc_log("got cached result for %s with status = %d\n", + output_file, status); + } + exit(status); +} + + +static char *find_compiler(const char *argv0) +{ + char *p; + char *base; + char *path, *tok; + struct stat st1, st2; + + /* we compare size, device and inode */ + if (stat("/proc/self/exe", &st1) != 0) { + cc_log("Can't stat ccache executable!?\n"); + exit(1); + } + + p = strrchr(argv0, '/'); + if (p) { + base = p+1; + } else { + base = argv0; + } + + path = getenv("PATH"); + if (!path) return NULL; + + path = x_strdup(path); + + /* search the path looking for the first compiler of the same name + that isn't us */ + for (tok=strtok(path,":"); tok; tok = strtok(NULL, ":")) { + char *fname; + x_asprintf(&fname, "%s/%s", tok, base); + if (stat(fname, &st2) == 0) { + if (st1.st_size != st2.st_size || + st1.st_dev != st2.st_dev || + st1.st_ino != st2.st_ino) { + /* found it! */ + free(path); + return fname; + } + } + } + + return NULL; +} + + +static void process_args(int argc, char **argv) +{ + int i; + int found_c_opt = 0; + char *input_file = NULL; + + stripped_args = args_init(); + + args_add(stripped_args, argv[0]); + + for (i=1; iargv = argv; + orig_args->argc = argc; + + /* process argument list, returning a new set of arguments for pre-processing */ + process_args(argc, argv); + + /* run with -E to find the hash */ + find_hash(stripped_args); + + /* if we can return from cache at this point then do */ + from_cache(1); + + /* run real compiler, semding output to cache */ + to_cache(stripped_args); + + /* return from cache */ + from_cache(0); + + /* oh oh! */ + cc_log("secondary from_cache failed!\n"); + failed(); +} + + +int main(int argc, char *argv[]) +{ + if (mkdir(cache_dir, 0755) != 0 && errno != EEXIST) { + fprintf(stderr,"Failed to create %s (%s)\n", + cache_dir, strerror(errno)); + exit(1); + } + ccache(argc, argv); + return 1; +} diff --git a/ccache.h b/ccache.h new file mode 100644 index 000000000..6c633312c --- /dev/null +++ b/ccache.h @@ -0,0 +1,56 @@ +#define _GNU_SOURCE + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STATUS_NOTFOUND 3 +#define STATUS_FATAL 4 +#define STATUS_NOCACHE 5 + + +#define CACHE_BASEDIR "/tmp/ccache" +#define CCACHE_LOGFILE "/tmp/ccache.log" + +typedef unsigned uint32; + +#include "mdfour.h" + +void hash_start(void); +void hash_string(const char *s); +void hash_file(const char *fname); +char *hash_result(void); + +int cc_log(const char *format, ...); +void fatal(const char *msg); +void oom(const char *msg); + +void copy_fd(int fd_in, int fd_out); + +void execute(char **argv, + const char *path_stdout, + const char *path_stderr, + const char *path_status); + +typedef struct { + char **argv; + int argc; +} ARGS; + + +#define x_asprintf asprintf +#define x_strdup strdup + +ARGS *args_init(void); +void args_add(ARGS *args, const char *s); + + diff --git a/execute.c b/execute.c new file mode 100644 index 000000000..98766fd73 --- /dev/null +++ b/execute.c @@ -0,0 +1,52 @@ +#include "ccache.h" + + +/* + execute a compiler backend, capturing all output to the given paths + the full path to the compiler to run is in argv[0] +*/ +void execute(char **argv, + const char *path_stdout, + const char *path_stderr, + const char *path_status) +{ + pid_t pid; + int fd; + int s, status; + + pid = fork(); + if (pid == -1) fatal("Failed to fork"); + + if (pid == 0) { + fd = open(path_stdout, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); + if (fd == -1) { + exit(STATUS_NOCACHE); + } + dup2(fd, 1); + close(fd); + + fd = open(path_stderr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); + if (fd == -1) { + exit(STATUS_NOCACHE); + } + dup2(fd, 2); + close(fd); + + exit(execv(argv[0], argv)); + } + + if (waitpid(pid, &status, 0) != pid) { + fatal("waitpid failed"); + } + + fd = open(path_status, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); + if (fd == -1) { + fatal("Failed to create status file"); + } + s = WEXITSTATUS(status); + if (write(fd, &s, sizeof(s)) != sizeof(s)) { + fatal("failed to write status file"); + } + close(fd); +} + diff --git a/hash.c b/hash.c new file mode 100644 index 000000000..125945ff4 --- /dev/null +++ b/hash.c @@ -0,0 +1,53 @@ +/* + simple front-end functions to mdfour code + Copyright tridge@samba.org 2002 + +*/ + +#include "ccache.h" + +static struct mdfour md; + +void hash_start(void) +{ + mdfour_begin(&md); +} + +void hash_string(const char *s) +{ + mdfour_update(&md, s, strlen(s)); +} + +/* add contents of a file to the hash */ +void hash_file(const char *fname) +{ + char buf[1024]; + int fd, n; + + fd = open(fname, O_RDONLY); + if (fd == -1) { + cc_log("Failed to open %s\n", fname); + fatal("hash_file"); + } + + while ((n = read(fd, buf, sizeof(buf))) > 0) { + mdfour_update(&md, buf, n); + } + close(fd); +} + +/* return the hash result as a static string */ +char *hash_result(void) +{ + unsigned char sum[16]; + static char ret[33]; + int i; + + mdfour_result(&md, sum); + + for (i=0;i<16;i++) { + snprintf(&ret[i*2], 3, "%02x", (unsigned)sum[i]); + } + + return ret; +} diff --git a/mdfour.c b/mdfour.c new file mode 100644 index 000000000..e79cac615 --- /dev/null +++ b/mdfour.c @@ -0,0 +1,256 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + a implementation of MD4 designed for use in the SMB authentication protocol + Copyright (C) Andrew Tridgell 1997-1998. + + 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. +*/ + +#include "ccache.h" + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +static struct mdfour *m; + +#define MASK32 (0xffffffff) + +#define F(X,Y,Z) ((((X)&(Y)) | ((~(X))&(Z)))) +#define G(X,Y,Z) ((((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)))) +#define H(X,Y,Z) (((X)^(Y)^(Z))) +#define lshift(x,s) (((((x)<<(s))&MASK32) | (((x)>>(32-(s)))&MASK32))) + +#define ROUND1(a,b,c,d,k,s) a = lshift((a + F(b,c,d) + M[k])&MASK32, s) +#define ROUND2(a,b,c,d,k,s) a = lshift((a + G(b,c,d) + M[k] + 0x5A827999)&MASK32,s) +#define ROUND3(a,b,c,d,k,s) a = lshift((a + H(b,c,d) + M[k] + 0x6ED9EBA1)&MASK32,s) + +/* this applies md4 to 64 byte chunks */ +static void mdfour64(uint32 *M) +{ + uint32 AA, BB, CC, DD; + uint32 A,B,C,D; + + A = m->A; B = m->B; C = m->C; D = m->D; + AA = A; BB = B; CC = C; DD = D; + + ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7); + ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19); + ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7); + ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19); + ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7); + ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19); + ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7); + ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19); + + + ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5); + ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13); + ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5); + ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13); + ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5); + ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13); + ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5); + ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13); + + ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9); + ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15); + ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9); + ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15); + ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9); + ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15); + ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9); + ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15); + + A += AA; B += BB; + C += CC; D += DD; + + A &= MASK32; B &= MASK32; + C &= MASK32; D &= MASK32; + + m->A = A; m->B = B; m->C = C; m->D = D; +} + +static void copy64(uint32 *M, const unsigned char *in) +{ + int i; + + for (i=0;i<16;i++) + M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | + (in[i*4+1]<<8) | (in[i*4+0]<<0); +} + +static void copy4(unsigned char *out,uint32 x) +{ + out[0] = x&0xFF; + out[1] = (x>>8)&0xFF; + out[2] = (x>>16)&0xFF; + out[3] = (x>>24)&0xFF; +} + +void mdfour_begin(struct mdfour *md) +{ + md->A = 0x67452301; + md->B = 0xefcdab89; + md->C = 0x98badcfe; + md->D = 0x10325476; + md->totalN = 0; +} + + +static void mdfour_tail(const unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32 M[16]; + uint32 b; + + m->totalN += n; + + b = m->totalN * 8; + + memset(buf, 0, 128); + if (n) memcpy(buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4(buf+56, b); + copy64(M, buf); + mdfour64(M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(M); + copy64(M, buf+64); + mdfour64(M); + } +} + +void mdfour_update(struct mdfour *md, const unsigned char *in, int n) +{ + uint32 M[16]; + + if (n == 0) mdfour_tail(in, n); + + m = md; + + while (n >= 64) { + copy64(M, in); + mdfour64(M); + in += 64; + n -= 64; + m->totalN += 64; + } + + if (n) mdfour_tail(in, n); +} + + +void mdfour_result(struct mdfour *md, unsigned char *out) +{ + m = md; + + copy4(out, m->A); + copy4(out+4, m->B); + copy4(out+8, m->C); + copy4(out+12, m->D); +} + + +void mdfour(unsigned char *out, unsigned char *in, int n) +{ + struct mdfour md; + mdfour_begin(&md); + mdfour_update(&md, in, n); + mdfour_result(&md, out); +} + +#ifdef TEST_MDFOUR +static void file_checksum1(char *fname) +{ + int fd, i; + struct mdfour md; + unsigned char buf[64*1024], sum[16]; + + fd = open(fname,O_RDONLY); + if (fd == -1) { + perror("fname"); + exit(1); + } + + mdfour_begin(&md); + + while (1) { + int n = read(fd, buf, sizeof(buf)); + if (n <= 0) break; + mdfour_update(&md, buf, n); + } + + close(fd); + + mdfour_result(&md, sum); + + for (i=0;i<16;i++) + printf("%02X", sum[i]); + printf("\n"); +} + +#if 0 +#include "../md4.h" + +static void file_checksum2(char *fname) +{ + int fd, i; + MDstruct md; + unsigned char buf[64], sum[16]; + + fd = open(fname,O_RDONLY); + if (fd == -1) { + perror("fname"); + exit(1); + } + + MDbegin(&md); + + while (1) { + int n = read(fd, buf, sizeof(buf)); + if (n <= 0) break; + MDupdate(&md, buf, n*8); + } + + if (!md.done) { + MDupdate(&md, buf, 0); + } + + close(fd); + + memcpy(sum, md.buffer, 16); + + for (i=0;i<16;i++) + printf("%02X", sum[i]); + printf("\n"); +} +#endif + + int main(int argc, char *argv[]) +{ + file_checksum1(argv[1]); +#if 0 + file_checksum2(argv[1]); +#endif + return 0; +} +#endif diff --git a/mdfour.h b/mdfour.h new file mode 100644 index 000000000..6cc9aae3b --- /dev/null +++ b/mdfour.h @@ -0,0 +1,34 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + a implementation of MD4 designed for use in the SMB authentication protocol + Copyright (C) Andrew Tridgell 1997-1998. + + 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. +*/ + +struct mdfour { + uint32 A, B, C, D; + uint32 totalN; +}; + +void mdfour_begin(struct mdfour *md); +void mdfour_update(struct mdfour *md, const unsigned char *in, int n); +void mdfour_result(struct mdfour *md, unsigned char *out); +void mdfour(unsigned char *out, unsigned char *in, int n); + + + + diff --git a/test.c b/test.c new file mode 100644 index 000000000..51801579a --- /dev/null +++ b/test.c @@ -0,0 +1,4 @@ +foo() +{ + printf("blah"); + diff --git a/util.c b/util.c new file mode 100644 index 000000000..52cdd0120 --- /dev/null +++ b/util.c @@ -0,0 +1,43 @@ +#include "ccache.h" + +static FILE *logfile; + +int cc_log(const char *format, ...) +{ + int ret; + va_list ap; + + if (!logfile) logfile = fopen(CCACHE_LOGFILE, "a"); + if (!logfile) return -1; + + va_start(ap, format); + ret = vfprintf(logfile, format, ap); + va_end(ap); + fflush(logfile); + + return ret; +} + +void fatal(const char *msg) +{ + cc_log("FATAL: %s\n", msg); + exit(1); +} + +void copy_fd(int fd_in, int fd_out) +{ + char buf[1024]; + int n; + + while ((n = read(fd_in, buf, sizeof(buf))) > 0) { + if (write(fd_out, buf, n) != n) { + fatal("Failed to copy fd"); + } + } +} + +void oom(const char *msg) +{ + cc_log("Out of memory: %s\n", msg); + exit(1); +}