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 \
--- /dev/null
+PROG=gcov-pull
+MAN=
+
+.include <bsd.prog.mk>
--- /dev/null
+/*
+ * gcov-pull - Request gcov data from server and write it to gcda files
+ * Author: Anton Kuijsten
+*/
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <lib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <string.h>
+#include <assert.h>
+#include <minix/gcov.h>
+
+#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 <pid>\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 <minix/gcov.h> 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;
+}
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
-#define NCALLS 112 /* number of system calls allowed */
+#define NCALLS 113 /* number of system calls allowed */
#define EXIT 1
#define FORK 2
*/
#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.
*/
* Miscellaneous field names *
*===========================================================================*/
-#define COMMON_RQ_BASE 0xE00
-
/* PM field names */
/* BRK */
#define PMBRK_ADDR m1_p1
#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 *
*===========================================================================*/
--- /dev/null
+#include <sys/types.h>
+#include <lib.h>
+#include <stdlib.h>
+#include <minix/syslib.h>
+
+/* 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);
/* 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) );
fts.c \
fgetln.c \
fsversion.c \
+ gcov.c \
+ gcov_flush.c \
getgrent.c \
getlogin.c \
getopt_long.c \
--- /dev/null
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <minix/gcov.h>
+
+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);
+}
+
--- /dev/null
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <minix/gcov.h>
+
+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.
+ */
+ ;
+}
+
sched_start.c \
sched_stop.c \
sef.c \
+ sef_gcov.c \
sef_init.c \
sef_liveupdate.c \
sef_ping.c \
profile.c \
vprintf.c \
timers.c \
- spin.c
+ spin.c \
+ gcov.c
CPPFLAGS.sched_start.c+= -I${MINIXSRCDIR}
--- /dev/null
+/* This code can be linked into minix servers that are compiled
+ * with gcc gcov flags.
+ * Author: Anton Kuijsten
+ */
+
+#include <lib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <minix/syslib.h>
+#include <minix/gcov.h>
+
+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 <size> bytes from <ptr> to <gcov_buff> */
+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);
+}
+
}
#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.
*/
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];
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
--- /dev/null
+
+#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;
+}
+
/* 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) );
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];
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
--- /dev/null
+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)
#
_MKVARS.no= \
MKCRYPTO_IDEA MKCRYPTO_MDC2 MKCRYPTO_RC5 MKDEBUG MKDEBUGLIB \
- MKEXTSRC \
+ MKEXTSRC MKCOVERAGE \
MKMANDOC MKMANZ MKOBJDIRS \
MKPCC MKPCCCMDS \
MKSOFTFLOAT MKSTRIPIDENT \
.ifndef HOSTPROG
.include <bsd.init.mk>
+.include <bsd.gcov.mk>
#
# Definitions and targets shared among all programs built by a single