
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
130 lines
2.9 KiB
C
130 lines
2.9 KiB
C
/*
|
|
* 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 <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 server_nr, command, size, result;
|
|
static char buff[BUFF_SZ]; /* Buffer for all the metadata and file data */
|
|
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: %s <label>\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.
|
|
*/
|
|
|
|
/* Fault in the complete buffer, so vm won't have to
|
|
manage the pages while flushing
|
|
*/
|
|
memset(buff, '\0', sizeof(buff));
|
|
|
|
buff_p = buff;
|
|
|
|
result = gcov_flush_svr(argv[1], buff_p, BUFF_SZ);
|
|
|
|
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;
|
|
}
|