gcov support, based on work contributed by Anton Kuijsten.

This commit is contained in:
Ben Gras 2010-08-25 13:06:43 +00:00
parent d8466ce31f
commit 5d6c2aae0a
23 changed files with 504 additions and 9 deletions

View file

@ -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 \

View file

@ -0,0 +1,4 @@
PROG=gcov-pull
MAN=
.include <bsd.prog.mk>

View file

@ -0,0 +1,131 @@
/*
* 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;
}

View file

@ -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

View file

@ -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.
*/

View file

@ -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 *
*===========================================================================*/

16
include/minix/gcov.h Normal file
View file

@ -0,0 +1,16 @@
#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);

View file

@ -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) );

View file

@ -57,6 +57,8 @@ SRCS+= \
fts.c \
fgetln.c \
fsversion.c \
gcov.c \
gcov_flush.c \
getgrent.c \
getlogin.c \
getopt_long.c \

55
lib/libc/other/gcov.c Normal file
View file

@ -0,0 +1,55 @@
#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);
}

View file

@ -0,0 +1,14 @@
#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.
*/
;
}

View file

@ -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}

161
lib/libsys/gcov.c Normal file
View file

@ -0,0 +1,161 @@
/* 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);
}

View file

@ -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.
*/

View file

@ -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];

View file

@ -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

73
servers/vfs/gcov.c Normal file
View file

@ -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;
}

View file

@ -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) );

View file

@ -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];

View file

@ -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

12
share/mk/bsd.gcov.mk Normal file
View file

@ -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)

View file

@ -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 \

View file

@ -4,6 +4,7 @@
.ifndef HOSTPROG
.include <bsd.init.mk>
.include <bsd.gcov.mk>
#
# Definitions and targets shared among all programs built by a single