
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
168 lines
3.6 KiB
C
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);
|
|
}
|