David van Moolenbroek 3ac58492b3 Add LLVM GCOV coverage support
With this patch, it is now possible to generate coverage information
for MINIX3 system services with LLVM.  In particular, the system can
be built with MKCOVERAGE=yes, either with a native "make build" or
with crosscompilation.  Either way, MKCOVERAGE=yes will build the
MINIX3 system services with coverage profiling support, generating a
.gcno file for each source module.  After a reboot it is possible to
obtain runtime coverage data (.gcda files) for individual system
services using gcov-pull(8).  The combination of the .gcno and .gcda
files can then be inspected with llvm-cov(1).

For reasons documented in minix.gcov.mk, only system service program
modules are supported for now; system service libraries (libsys etc.)
are not included.  Userland programs are not affected by MKCOVERAGE.

The heart of this patch is the libsys code that writes data generated
by the LLVM coverage hooks into a serialized format using the routines
we already had for GCC GCOV.  Unfortunately, the new llvm_gcov.c code
is LLVM ABI dependent, and may therefore have to be updated later when
we upgrade LLVM.  The current implementation should support all LLVM
versions 3.x with x >= 4.

The rest of this patch is mostly a light cleanup of our existing GCOV
infrastructure, with as most visible change that gcov-pull(8) now
takes a service label string rather than a PID number.

Change-Id: I6de055359d3d2b3f53e426f3fffb17af7877261f
2016-09-24 22:18:31 +00:00

168 lines
3.6 KiB
C

/* 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/sysutil.h>
#include <minix/gcov.h>
static endpoint_t endpt = NONE; /* endpoint of requesting service, or NONE */
static cp_grant_id_t grant; /* grant to write results into during request */
static int pos; /* data-buffer pointer from user space tool */
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(const void *ptr, int size)
{
int r;
assert(endpt != NONE);
assert(pos <= gcov_buff_sz);
if(pos+size > gcov_buff_sz) {
size = pos - gcov_buff_sz;
}
r = sys_safecopyto(endpt, grant, pos, (vir_bytes)ptr, size);
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(const char *name, const char *mode)
{
if(!gcov_enable) return NULL;
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(const void *ptr, size_t itemsize, size_t nitems,
FILE *stream)
{
int size = itemsize * nitems;
if(!gcov_enable) return -1;
/* 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 EOF;
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(endpoint_t ep, cp_grant_id_t grantid, size_t bufsize)
{
/* Initialize global state. */
pos=0;
endpt = ep;
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;
endpt = NONE;
/* 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);
replymsg.m_type = gcov_flush(msg->m_source, msg->m_vfs_lsys_gcov.grant,
msg->m_vfs_lsys_gcov.size);
return ipc_send(msg->m_source, &replymsg);
}