From: Ben Gras Date: Wed, 25 Aug 2010 13:06:43 +0000 (+0000) Subject: gcov support, based on work contributed by Anton Kuijsten. X-Git-Tag: v3.1.8~44 X-Git-Url: http://zhaoyanbai.com/repos/man.named-journalprint.html?a=commitdiff_plain;h=5d6c2aae0a6245642ce747966189b4f673236b41;p=minix.git gcov support, based on work contributed by Anton Kuijsten. --- diff --git a/commands/Makefile b/commands/Makefile index 4030cd652..0a82de12f 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -12,7 +12,7 @@ SUBDIR= aal add_route adduser advent arp ash at autil awk \ dhrystone diff dirname dis88 diskctl du dumpcore easypack \ ed eject elle elvis env expand factor file \ find finger fingerd fix fold format fortune fsck.mfs \ - fsck1 ftp101 ftpd200 getty grep gomoku head host \ + fsck1 ftp101 ftpd200 gcov-pull getty grep gomoku head host \ hostaddr id ifconfig ifdef indent install \ intr ipcrm ipcs irdpd isoread join kill last leave \ less lex life loadkeys loadramdisk logger login look lp \ diff --git a/commands/gcov-pull/Makefile b/commands/gcov-pull/Makefile new file mode 100644 index 000000000..8a121bd58 --- /dev/null +++ b/commands/gcov-pull/Makefile @@ -0,0 +1,4 @@ +PROG=gcov-pull +MAN= + +.include diff --git a/commands/gcov-pull/gcov-pull.c b/commands/gcov-pull/gcov-pull.c new file mode 100644 index 000000000..8739451af --- /dev/null +++ b/commands/gcov-pull/gcov-pull.c @@ -0,0 +1,131 @@ +/* + * gcov-pull - Request gcov data from server and write it to gcda files + * Author: Anton Kuijsten +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFF_SZ (4 * 1024 * 1024) /* 4MB */ + +int read_int(void); + +char *buff_p; + +/* helper function to read int from the buffer */ +int read_int(void) +{ + int res; + memcpy(&res, buff_p, sizeof(int)); + buff_p += sizeof(int); + return res; +} + +int main(int argc, char *argv[]) +{ + FILE *fd = NULL; + int i, server_nr, command, size, result; + char buff[BUFF_SZ]; /* Buffer for all the metadata and file data sent */ + message msg; /* message sent to vfs */ + + if(argc!=2 || sscanf(argv[1], "%d", &server_nr)!=1) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + /* + When making a GCOV call to a server, the gcov library linked into + the server will try to write gcov data to disk. This writing is + normally done with calls to the vfs, using stdio library calls. + This is not correct behaviour for servers, especially vfs itself. + Therefore, the server catches those attempts. The messages used for + this communication are stored in a buffer. When the gcov operation + is done, the buffer is copied from the server to this user space, + from where the calls are finally made to the vfs. GCOV calls to the + various servers are all routed trough vfs. For more information, see + the header file. + */ + + /* visit complete buffer, so vm won't has to + manage the pages while flushing + */ + memset(buff, 'a', sizeof(buff)); + + buff_p = buff; + + result = gcov_flush_svr(buff_p, BUFF_SZ, server_nr); + + if(result >= BUFF_SZ) { + fprintf(stderr, "Too much data to hold in buffer: %d\n", result); + fprintf(stderr, "Maximum: %d\n", BUFF_SZ); + return 1; + } + + if(result < 0) { + fprintf(stderr, "Call failed\n"); + return 1; + } + + /* At least GCOVOP_END opcode expected. */ + if(result < sizeof(int)) { + fprintf(stderr, "Invalid gcov data from pid %d\n", server_nr); + return 1; + } + + /* Only GCOVOP_END is valid but empty. */ + if(result == sizeof(int)) { + fprintf(stderr, "no gcov data.\n"); + return 0; + } + + /* Iterate through the system calls contained in the buffer, + * and execute them + */ + while((command=read_int()) != GCOVOP_END) { + char *fn; + switch(command) { + case GCOVOP_OPEN: + size = read_int(); + fn = buff_p; + if(strchr(fn, '/')) { + fn = strrchr(fn, '/'); + assert(fn); + fn++; + } + assert(fn); + if(!(fd = fopen(fn, "w+"))) { + perror(buff_p); + exit(1); + } + buff_p += size; + break; + case GCOVOP_CLOSE: + if(!fd) { + fprintf(stderr, "bogus close\n"); + exit(1); + } + fclose(fd); + fd = NULL; + break; + case GCOVOP_WRITE: + size = read_int(); + fwrite(buff_p, size, 1, fd); + buff_p += size; + break; + default: + fprintf(stderr, "bogus command %d in buffer.\n", + command); + exit(1); + } + } + + return 0; +} diff --git a/include/Makefile b/include/Makefile index a75437ea8..6763fe899 100644 --- a/include/Makefile +++ b/include/Makefile @@ -24,7 +24,7 @@ INCS+= minix/a.out.h minix/bitmap.h minix/callnr.h minix/cdrom.h \ minix/rs.h minix/safecopies.h minix/sched.h minix/sef.h minix/sound.h \ minix/spin.h minix/sys_config.h minix/sysinfo.h minix/syslib.h \ minix/sysutil.h minix/timers.h minix/tty.h minix/type.h minix/types.h \ - minix/u64.h minix/vfsif.h minix/vm.h minix/vtreefs.h \ + minix/u64.h minix/vfsif.h minix/vm.h minix/vtreefs.h minix/gcov.h \ minix/compiler.h minix/compiler-ack.h minix/sha2.h minix/sha1.h minix/md5.h \ minix/audio_fw.h INCS+= net/hton.h net/if.h net/ioctl.h net/netlib.h diff --git a/include/minix/callnr.h b/include/minix/callnr.h index 3b52911ad..58c37c7dd 100644 --- a/include/minix/callnr.h +++ b/include/minix/callnr.h @@ -1,4 +1,4 @@ -#define NCALLS 112 /* number of system calls allowed */ +#define NCALLS 113 /* number of system calls allowed */ #define EXIT 1 #define FORK 2 @@ -113,6 +113,8 @@ */ #define SRV_KILL 111 /* to PM: special kill call for RS */ +#define GCOV_FLUSH 112 /* flush gcov data from server to gcov files */ + #define TASK_REPLY 121 /* to VFS: reply code from drivers, not * really a standalone call. */ diff --git a/include/minix/com.h b/include/minix/com.h index 454da85e7..04f289e44 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -864,8 +864,6 @@ * Miscellaneous field names * *===========================================================================*/ -#define COMMON_RQ_BASE 0xE00 - /* PM field names */ /* BRK */ #define PMBRK_ADDR m1_p1 @@ -891,10 +889,19 @@ #define SEL_ERRORFDS m8_p3 #define SEL_TIMEOUT m8_p4 +#define COMMON_RQ_BASE 0xE00 + /* Field names for system signals (sent by a signal manager). */ #define SIGS_SIGNAL_RECEIVED (COMMON_RQ_BASE+0) # define SIGS_SIG_NUM m2_i1 +/* Common request to all processes: gcov data. */ +#define COMMON_REQ_GCOV_DATA (COMMON_RQ_BASE+1) +# define GCOV_GRANT m1_i2 +# define GCOV_PID m1_i3 +# define GCOV_BUFF_P m1_p1 +# define GCOV_BUFF_SZ m1_i1 + /*===========================================================================* * Messages for VM server * *===========================================================================*/ diff --git a/include/minix/gcov.h b/include/minix/gcov.h new file mode 100644 index 000000000..51b11c644 --- /dev/null +++ b/include/minix/gcov.h @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +/* opcodes for use in gcov buffer */ +#define GCOVOP_OPEN 23 +#define GCOVOP_WRITE 24 +#define GCOVOP_CLOSE 25 +#define GCOVOP_END 26 + +/* More information on the GCOV Minix Wiki page. */ + +int gcov_flush_svr(char *buff, int buff_sz, int server_nr); +extern void __gcov_flush (void); +int do_gcov_flush_impl(message *msg); diff --git a/include/minix/sef.h b/include/minix/sef.h index 3cb4041a2..9bb2414b1 100644 --- a/include/minix/sef.h +++ b/include/minix/sef.h @@ -199,10 +199,12 @@ _PROTOTYPE( int sef_cb_lu_response_rs_reply, (message *m_ptr) ); /* Callback type definitions. */ typedef void(*sef_cb_signal_handler_t)(int signo); typedef int(*sef_cb_signal_manager_t)(endpoint_t target, int signo); +typedef int(*sef_cb_gcov_t)(message *msg); /* Callback registration helpers. */ _PROTOTYPE( void sef_setcb_signal_handler, (sef_cb_signal_handler_t cb)); _PROTOTYPE( void sef_setcb_signal_manager, (sef_cb_signal_manager_t cb)); +_PROTOTYPE( void sef_setcb_gcov, (sef_cb_gcov_t cb)); /* Predefined callback implementations. */ _PROTOTYPE( void sef_cb_signal_handler_null, (int signo) ); diff --git a/lib/libc/other/Makefile.inc b/lib/libc/other/Makefile.inc index 8f18fb62a..d8d902692 100644 --- a/lib/libc/other/Makefile.inc +++ b/lib/libc/other/Makefile.inc @@ -57,6 +57,8 @@ SRCS+= \ fts.c \ fgetln.c \ fsversion.c \ + gcov.c \ + gcov_flush.c \ getgrent.c \ getlogin.c \ getopt_long.c \ diff --git a/lib/libc/other/gcov.c b/lib/libc/other/gcov.c new file mode 100644 index 000000000..b49657ca9 --- /dev/null +++ b/lib/libc/other/gcov.c @@ -0,0 +1,55 @@ +#include +#include +#include + +#include + +int gcov_flush_svr(char *buff, int buff_sz, int server_nr) +{ + message msg; + + msg.GCOV_BUFF_P = buff; + msg.GCOV_BUFF_SZ = buff_sz; + msg.GCOV_PID = server_nr; + + /* Make the call to server. It will call the gcov library, + * buffer the stdio requests, and copy the buffer to this user + * space + */ + _syscall(VFS_PROC_NR, GCOV_FLUSH, &msg); + return; +} + + +/* wrappers for file system calls from gcc libgcov library. + Default calls are wrapped. In libsys, an alternative + implementation for servers is used. +*/ + +FILE *_gcov_fopen(char *name, char *mode){ + return fopen(name, mode); +} + + +size_t _gcov_fread(void *ptr, size_t itemsize, size_t nitems + , FILE *stream){ + return fread(ptr, itemsize, nitems, stream); +} + +size_t _gcov_fwrite(void *ptr, size_t itemsize, size_t nitems + , FILE *stream){ + return fwrite(ptr, itemsize, nitems, stream); +} + +int _gcov_fclose(FILE *stream){ + return fclose(stream); +} + +int _gcov_fseek(FILE *stream, long offset, int ptrname){ + return fseek(stream, offset, ptrname); +} + +char *_gcov_getenv(const char *name){ + return getenv(name); +} + diff --git a/lib/libc/other/gcov_flush.c b/lib/libc/other/gcov_flush.c new file mode 100644 index 000000000..60e678c2d --- /dev/null +++ b/lib/libc/other/gcov_flush.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include + +void __gcov_flush(void) +{ + /* A version of __gcov_flush for cases in which no gcc -lgcov + * is given; i.e. non-gcc or gcc without active gcov. + */ + ; +} + diff --git a/lib/libsys/Makefile b/lib/libsys/Makefile index 7469515e4..e33d25942 100644 --- a/lib/libsys/Makefile +++ b/lib/libsys/Makefile @@ -30,6 +30,7 @@ SRCS= \ sched_start.c \ sched_stop.c \ sef.c \ + sef_gcov.c \ sef_init.c \ sef_liveupdate.c \ sef_ping.c \ @@ -120,7 +121,8 @@ SRCS= \ profile.c \ vprintf.c \ timers.c \ - spin.c + spin.c \ + gcov.c CPPFLAGS.sched_start.c+= -I${MINIXSRCDIR} diff --git a/lib/libsys/gcov.c b/lib/libsys/gcov.c new file mode 100644 index 000000000..2d020b343 --- /dev/null +++ b/lib/libsys/gcov.c @@ -0,0 +1,161 @@ +/* This code can be linked into minix servers that are compiled + * with gcc gcov flags. + * Author: Anton Kuijsten + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int grant, pos; /* data-buffer pointer from user space tool */ +static int gcov_fd=0; /* file descriptor for writing gcov data */ +static int gcov_enable=0; /* nothing will be done with gcov-data if zero */ +static int gcov_buff_sz; /* size of user space buffer */ +static FILE gcov_file; /* used as fopen() return value. */ +static int gcov_opened; + +/* copies bytes from to */ +static void add_buff(void *ptr, int size) +{ + int r; + assert(pos <= gcov_buff_sz); + + if(pos+size > gcov_buff_sz) { + size = pos - gcov_buff_sz; + } + + r = sys_safecopyto(VFS_PROC_NR, grant, pos, (vir_bytes)ptr, size, D); + + if(r) { + printf("libsys: gcov: safecopy failed (%d)\n", r); + } + + pos += size; + + assert(pos <= gcov_buff_sz); +} + +/* easy wrapper for add_buff */ +static void add_int(int value) +{ + add_buff((void *) &value, sizeof(int)); +} + +/* These functions are meant to replace standard file + * system calls (fopen, etc) + */ + +FILE *_gcov_fopen(char *name, char *mode) +{ + if(!gcov_enable) return; + + assert(!gcov_opened); + + /* write information to buffer */ + add_int(GCOVOP_OPEN); + add_int(strlen(name)+1); + add_buff(name, strlen(name)+1); + + gcov_opened = 1; + + /* return dummy FILE *. */ + return &gcov_file; +} + + +size_t _gcov_fread(void *ptr, size_t itemsize, size_t nitems, FILE *stream) +{ + return 0; +} + +size_t _gcov_fwrite(void *ptr, size_t itemsize, size_t nitems, FILE *stream) +{ + int size = itemsize * nitems; + + if(!gcov_enable) return; + + /* only have one file open at a time to ensure writes go + * to the right place. + */ + assert(gcov_opened); + assert(stream == &gcov_file); + + /* write information to buffer */ + add_int(GCOVOP_WRITE); + add_int(size); + add_buff(ptr, size); + + return nitems; +} + +int _gcov_fclose(FILE *stream) +{ + if(!gcov_enable) return; + + add_int(GCOVOP_CLOSE); + assert(gcov_opened); + gcov_opened = 0; + return 0; +} + +int _gcov_fseek(FILE *stream, long offset, int ptrname) +{ + return 0; +} + +char *_gcov_getenv(const char *name) +{ + return NULL; +} + +int gcov_flush(cp_grant_id_t grantid, int bufsize) +{ + /* Initialize global state. */ + pos=0; + grant = grantid; + gcov_buff_sz = bufsize; + assert(!gcov_enable); + assert(!gcov_opened); + gcov_enable = 1; + + /* Trigger copying. + * This function is not always available, but there is a do-nothing + * version in libc so that executables can be linked even without + * this code ever being activated. + */ + __gcov_flush(); + + /* Mark the end of the data, stop. */ + add_int(GCOVOP_END); + assert(!gcov_opened); + assert(gcov_enable); + gcov_enable = 0; + + /* Return number of bytes used in buffer. */ + return pos; +} + +/* This function can be called to perform the copying. + * It sends its own reply message and can thus be + * registered as a SEF * callback. + */ +int do_gcov_flush_impl(message *msg) +{ + message replymsg; + memset(&replymsg, 0, sizeof(replymsg)); + + assert(msg->m_type == COMMON_REQ_GCOV_DATA); + assert(msg->m_source == VFS_PROC_NR); + + replymsg.m_type = gcov_flush(msg->GCOV_GRANT, msg->GCOV_BUFF_SZ); + return send(msg->m_source, &replymsg); +} + diff --git a/lib/libsys/sef.c b/lib/libsys/sef.c index 428b076fd..5be45ae6f 100644 --- a/lib/libsys/sef.c +++ b/lib/libsys/sef.c @@ -151,6 +151,14 @@ PUBLIC int sef_receive_status(endpoint_t src, message *m_ptr, int *status_ptr) } #endif + /* Intercept GCOV data requests (sent by VFS in vfs/gcov.c). */ + if(m_ptr->m_type == COMMON_REQ_GCOV_DATA && + m_ptr->m_source == VFS_PROC_NR) { + if(do_sef_gcov_request(m_ptr) == OK) { + continue; + } + } + /* If we get this far, this is not a valid SEF request, return and * let the caller deal with that. */ diff --git a/servers/pm/table.c b/servers/pm/table.c index 0e3e0eafd..8fdc6683d 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -124,6 +124,7 @@ _PROTOTYPE (int (*call_vec[]), (void) ) = { do_deldma, /* 109 = deldma */ do_getdma, /* 110 = getdma */ do_srv_kill, /* 111 = srv_kill */ + no_sys, /* 112 = gcov_flush */ }; /* This should not fail with "array size is negative": */ extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1]; diff --git a/servers/vfs/Makefile b/servers/vfs/Makefile index ae1806016..db545d7b8 100644 --- a/servers/vfs/Makefile +++ b/servers/vfs/Makefile @@ -4,7 +4,7 @@ SRCS= main.c open.c read.c write.c pipe.c dmap.c \ path.c device.c mount.c link.c exec.c \ filedes.c stadir.c protect.c time.c \ lock.c misc.c utility.c select.c table.c \ - vnode.c vmnt.c request.c fscall.c + vnode.c vmnt.c request.c fscall.c gcov.c DPADD+= ${LIBSYS} ${LIBTIMERS} LDADD+= -lsys -ltimers diff --git a/servers/vfs/gcov.c b/servers/vfs/gcov.c new file mode 100644 index 000000000..1bbe0b411 --- /dev/null +++ b/servers/vfs/gcov.c @@ -0,0 +1,73 @@ + +#include "fs.h" +#include "file.h" +#include "fproc.h" + + +/*===========================================================================* + * do_gcov_flush * + *===========================================================================*/ +PUBLIC int do_gcov_flush() +{ +/* A userland tool has requested the gcov data from another + * process (possibly vfs itself). Grant the target process + * access to the supplied buffer, and perform the call that + * makes the target copy its buffer to the caller (incl vfs + * itself). + */ + int i; + struct fproc *rfp; + ssize_t size; + cp_grant_id_t grantid; + int r; + int n; + pid_t target; + + size = m_in.GCOV_BUFF_SZ; + target = m_in.GCOV_PID; + + /* If the wrong process is sent to, the system hangs; + * so make this root-only. + */ + + if (!super_user) return(EPERM); + + /* Find target gcov process. */ + + for(n = 0; n < NR_PROCS; n++) { + if(fproc[n].fp_endpoint != NONE && + fproc[n].fp_pid == target) + break; + } + + if(n >= NR_PROCS) { + printf("VFS: gcov proccess %d not found.\n", target); + return ESRCH; + } + + rfp = &fproc[n]; + + /* Grant target process to requestor's buffer. */ + + if((grantid = cpf_grant_magic(rfp->fp_endpoint, + who_e, (vir_bytes) m_in.GCOV_BUFF_P, + size, CPF_WRITE)) < 0) { + printf("VFS: gcov_flush: grant failed\n"); + return ENOMEM; + } + + if(target == getpid()) { + /* Request is for VFS itself. */ + r = gcov_flush(grantid, size); + } else { + /* Perform generic GCOV request. */ + m_out.GCOV_GRANT = grantid; + m_out.GCOV_BUFF_SZ = size; + r = _taskcall(rfp->fp_endpoint, COMMON_REQ_GCOV_DATA, &m_out); + } + + cpf_revoke(grantid); + + return r; +} + diff --git a/servers/vfs/proto.h b/servers/vfs/proto.h index 0a107e4c9..10fd86194 100644 --- a/servers/vfs/proto.h +++ b/servers/vfs/proto.h @@ -257,6 +257,9 @@ _PROTOTYPE( int check_vrefs, (void) ); /* write.c */ _PROTOTYPE( int do_write, (void) ); +/* gcov.c */ +_PROTOTYPE( int do_gcov_flush, (void) ); + /* select.c */ _PROTOTYPE( int do_select, (void) ); _PROTOTYPE( int select_callback, (struct filp *, int ops) ); diff --git a/servers/vfs/table.c b/servers/vfs/table.c index 6d7f4f28a..2c7c3e413 100644 --- a/servers/vfs/table.c +++ b/servers/vfs/table.c @@ -128,6 +128,7 @@ PUBLIC _PROTOTYPE (int (*call_vec[]), (void) ) = { no_sys, /* 109 = (deldma) */ no_sys, /* 110 = (getdma) */ no_sys, /* 111 = (srv_kill) */ + do_gcov_flush, /* 112 = gcov_flush */ }; /* This should not fail with "array size is negative": */ extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1]; diff --git a/share/mk/Makefile b/share/mk/Makefile index ecd7d67c2..36de72ec8 100644 --- a/share/mk/Makefile +++ b/share/mk/Makefile @@ -7,7 +7,7 @@ FILES= bsd.ack.mk bsd.dep.mk bsd.files.mk \ bsd.gcc.mk bsd.inc.mk \ bsd.init.mk bsd.kinc.mk bsd.klinks.mk \ bsd.lib.mk bsd.links.mk bsd.man.mk bsd.obj.mk bsd.own.mk \ - bsd.prog.mk bsd.subdir.mk bsd.sys.mk \ + bsd.prog.mk bsd.subdir.mk bsd.sys.mk bsd.gcov.mk \ sys.mk FILESDIR=/usr/share/mk diff --git a/share/mk/bsd.gcov.mk b/share/mk/bsd.gcov.mk new file mode 100644 index 000000000..f466bec19 --- /dev/null +++ b/share/mk/bsd.gcov.mk @@ -0,0 +1,12 @@ +LCOV=lcov.$(PROG) +CLEANFILES+= *.gcno *.gcda $(LCOV) + +.if ${MKCOVERAGE} == "yes" +CFLAGS+=-fno-builtin -fprofile-arcs -ftest-coverage +LDADD+= -lgcov +COMPILER_TYPE=gnu +CC=gcc +.endif + +lcov: + lcov -c -d . >$(LCOV) diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 83390da3a..ff512aedf 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -749,7 +749,7 @@ ${var}?= yes # _MKVARS.no= \ MKCRYPTO_IDEA MKCRYPTO_MDC2 MKCRYPTO_RC5 MKDEBUG MKDEBUGLIB \ - MKEXTSRC \ + MKEXTSRC MKCOVERAGE \ MKMANDOC MKMANZ MKOBJDIRS \ MKPCC MKPCCCMDS \ MKSOFTFLOAT MKSTRIPIDENT \ diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index 04f8eecca..d3eb82457 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -4,6 +4,7 @@ .ifndef HOSTPROG .include +.include # # Definitions and targets shared among all programs built by a single