Fix coverage collection

This commit is contained in:
Ivan Baravy 2024-03-02 14:28:47 +00:00
parent 0073eb0cc9
commit 78db00fef9
12 changed files with 100 additions and 65 deletions

4
README
View File

@ -117,6 +117,10 @@ Allow reading process_vm_readv syscall.
# sysctl -w kernel.yama.ptrace_scope=0 # sysctl -w kernel.yama.ptrace_scope=0
Insert msr linux kernel module to collect coverage.
# modprobe msr
Links & Acknowledgements Links & Acknowledgements
------------------------ ------------------------

View File

@ -28,7 +28,8 @@ else
endif endif
CFLAGS=$(WARNINGS) $(NOWARNINGS) -std=c11 -g -O0 -DNDEBUG -masm=intel \ CFLAGS=$(WARNINGS) $(NOWARNINGS) -std=c11 -g -O0 -DNDEBUG -masm=intel \
-D_POSIX_C_SOURCE=200809L -I$(HOST) -Ideps -I. -fno-pie -D_POSIX -D_POSIX_C_SOURCE=200809L -I$(HOST) -Ideps -I. -fno-pie -D_POSIX \
-fno-common
CFLAGS_32=$(CFLAGS) -m32 -D_FILE_OFFSET_BITS=64 -D__USE_TIME_BITS64 CFLAGS_32=$(CFLAGS) -m32 -D_FILE_OFFSET_BITS=64 -D__USE_TIME_BITS64
LDFLAGS=-no-pie LDFLAGS=-no-pie
LDFLAGS_32=$(LDFLAGS) -m32 LDFLAGS_32=$(LDFLAGS) -m32

View File

@ -37,6 +37,7 @@ _Thread_local char reffname[PATH_MAX];
_Thread_local char outfname[PATH_MAX]; _Thread_local char outfname[PATH_MAX];
int coverage = 0; int coverage = 0;
int no_timeout = 0;
int silent_success = 1; int silent_success = 1;
static int static int
@ -125,7 +126,7 @@ struct test_wait_arg {
pthread_cond_t *cond; pthread_cond_t *cond;
}; };
static unsigned static time_t
get_test_timeout(const char *testname) { get_test_timeout(const char *testname) {
sprintf(timeoutfname, "%s/%s", testname, TIMEOUT_FILENAME); sprintf(timeoutfname, "%s/%s", testname, TIMEOUT_FILENAME);
FILE *f = fopen(timeoutfname, "rb"); FILE *f = fopen(timeoutfname, "rb");
@ -178,21 +179,33 @@ run_test(const void *arg) {
int child; int child;
if (!(child = fork())) { if (!(child = fork())) {
chdir(test_name); chdir(test_name);
execl("../../umka_shell", "../../umka_shell", "-ri", "run.us", "-o", if (coverage) {
"out.log", NULL); char covfile[64];
sprintf(covfile, "../cov_%s", test_name);
execl("/usr/bin/taskset", "taskset", "1", "sudo",
"../../umka_shell", "-ri", "run.us", "-o", "out.log", "-c",
covfile, NULL);
} else {
execl("../../umka_shell", "../../umka_shell", "-ri", "run.us", "-o",
"out.log", NULL);
}
fprintf(stderr, "Can't run test command: %s\n", strerror(errno)); fprintf(stderr, "Can't run test command: %s\n", strerror(errno));
return (void *)-1; return (void *)-1;
} }
pthread_t t; pthread_t t;
struct test_wait_arg wa = {.pid = child, .cond = &cond}; struct test_wait_arg wa = {.pid = child, .cond = &cond};
pthread_create(&t, NULL, thread_wait, &wa); pthread_create(&t, NULL, thread_wait, &wa);
unsigned tout = get_test_timeout(test_name); time_t tout = get_test_timeout(test_name);
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts); clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += tout; if (no_timeout) {
ts.tv_sec += 60*60*24*7; // a week
} else {
ts.tv_sec += tout;
}
int status; int status;
if ((status = pthread_cond_timedwait(&cond, &mutex, &ts))) { if ((status = pthread_cond_timedwait(&cond, &mutex, &ts))) {
fprintf(stderr, "[!] %s: timeout (%um%us)\n", test_name, fprintf(stderr, "[!] %s: timeout (%llim%llis)\n", test_name,
tout/60, tout % 60); tout/60, tout % 60);
kill(child, SIGKILL); kill(child, SIGKILL);
result = (void *)(intptr_t)status; result = (void *)(intptr_t)status;
@ -270,19 +283,24 @@ main(int argc, char *argv[]) {
(void)argc; (void)argc;
// int action = ACTION_RUN; // int action = ACTION_RUN;
size_t nthreads = 1; size_t nthreads = 1;
struct optparse opts; struct optparse opts;
optparse_init(&opts, argv); optparse_init(&opts, argv);
int opt; int opt;
while ((opt = optparse(&opts, "j:s")) != -1) { while ((opt = optparse(&opts, "cj:st")) != -1) {
switch (opt) { switch (opt) {
case 'c':
coverage = 1;
break;
case 'j': case 'j':
nthreads = strtoul(opts.optarg, NULL, 0); nthreads = strtoul(opts.optarg, NULL, 0);
break; break;
case 's': case 's':
silent_success = 0; silent_success = 0;
break; break;
case 't':
no_timeout = 1;
break;
default: default:
fprintf(stderr, "[!] Unknown option: '%c'\n", opt); fprintf(stderr, "[!] Unknown option: '%c'\n", opt);
exit(1); exit(1);

View File

@ -4368,7 +4368,9 @@ shell_run_cmd_sync(struct shell_ctx *ctx) {
} }
case UMKA_CMD_SYS_LFN: { case UMKA_CMD_SYS_LFN: {
struct cmd_sys_lfn_arg *c = &cmd->sys_lfn.arg; struct cmd_sys_lfn_arg *c = &cmd->sys_lfn.arg;
COVERAGE_ON();
umka_sys_lfn(c->bufptr, c->r, c->f70or80); umka_sys_lfn(c->bufptr, c->r, c->f70or80);
COVERAGE_OFF();
break; break;
} }
default: default:

24
trace.c
View File

@ -8,24 +8,22 @@
#include "trace_lbr.h" #include "trace_lbr.h"
uint32_t coverage;
void void
trace_begin(void) { trace_enable(void) {
trace_lbr_begin(); trace_lbr_enable();
} }
void void
trace_end(void) { trace_disable(void) {
trace_lbr_end(); trace_lbr_disable();
}
uint32_t
trace_pause(void) {
return trace_lbr_pause();
} }
void void
trace_resume(uint32_t value) { trace_on(void) {
trace_lbr_resume(value); trace_lbr_on();
}
void
trace_off(void) {
trace_lbr_off();
} }

16
trace.h
View File

@ -9,17 +9,13 @@
#ifndef TRACE_H_INCLUDED #ifndef TRACE_H_INCLUDED
#define TRACE_H_INCLUDED #define TRACE_H_INCLUDED
#include <inttypes.h> #define COVERAGE_ON() do { trace_on(); } while (0)
extern uint32_t coverage; #define COVERAGE_OFF() do { trace_off(); } while (0)
#define COVERAGE_ON() do { trace_resume(coverage); } while (0) void trace_enable(void);
void trace_disable(void);
#define COVERAGE_OFF() do { coverage = trace_pause(); } while (0) void trace_on(void);
void trace_off(void);
void trace_begin(void);
void trace_end(void);
uint32_t trace_pause(void);
void trace_resume(uint32_t value);
#endif #endif

View File

@ -24,10 +24,11 @@
#define MSR_IA32_LASTBRANCHFROMIP 0x1db #define MSR_IA32_LASTBRANCHFROMIP 0x1db
#define MSR_IA32_LASTBRANCHTOIP 0x1dc #define MSR_IA32_LASTBRANCHTOIP 0x1dc
int coverage = 0;
int msrfd; int msrfd;
uint64_t rdmsr(uint32_t reg) static uint64_t
{ rdmsr(uint32_t reg) {
uint64_t data = 0; uint64_t data = 0;
#ifndef _WIN32 #ifndef _WIN32
@ -43,8 +44,8 @@ uint64_t rdmsr(uint32_t reg)
return data; return data;
} }
void wrmsr(uint32_t reg, uint64_t data) static void
{ wrmsr(uint32_t reg, uint64_t data) {
#ifndef _WIN32 #ifndef _WIN32
int fd; int fd;
fd = open("/dev/cpu/0/msr", O_WRONLY); fd = open("/dev/cpu/0/msr", O_WRONLY);
@ -66,7 +67,8 @@ void wrmsr(uint32_t reg, uint64_t data)
#endif #endif
} }
void handle_sigtrap(int signo) { static void
handle_sigtrap(int signo) {
(void)signo; (void)signo;
#ifndef _WIN32 #ifndef _WIN32
uint64_t from = rdmsr(MSR_IA32_LASTBRANCHFROMIP); uint64_t from = rdmsr(MSR_IA32_LASTBRANCHFROMIP);
@ -86,13 +88,13 @@ void handle_sigtrap(int signo) {
#endif #endif
} }
uint32_t set_eflags_tf(uint32_t tf); void
trace_lbr_enable(void) {
void trace_lbr_begin(void) {
#ifndef _WIN32 #ifndef _WIN32
struct sigaction action; struct sigaction action;
action.sa_handler = &handle_sigtrap; action.sa_handler = &handle_sigtrap;
action.sa_flags = 0; action.sa_flags = 0;
coverage = 1;
sigaction(SIGTRAP, &action, NULL); sigaction(SIGTRAP, &action, NULL);
wrmsr(MSR_IA32_DEBUGCTL, MSR_IA32_DEBUGCTL_LBR + MSR_IA32_DEBUGCTL_BTF); wrmsr(MSR_IA32_DEBUGCTL, MSR_IA32_DEBUGCTL_LBR + MSR_IA32_DEBUGCTL_BTF);
@ -106,7 +108,8 @@ void trace_lbr_begin(void) {
#endif #endif
} }
void trace_lbr_end(void) { void
trace_lbr_disable(void) {
#ifndef _WIN32 #ifndef _WIN32
wrmsr(MSR_IA32_DEBUGCTL, 0); wrmsr(MSR_IA32_DEBUGCTL, 0);
close(msrfd); close(msrfd);
@ -115,10 +118,12 @@ void trace_lbr_end(void) {
#endif #endif
} }
uint32_t trace_lbr_pause(void) { void
return set_eflags_tf(0u); trace_lbr_on(void) {
set_eflags_tf(coverage);
} }
void trace_lbr_resume(uint32_t value) { void
set_eflags_tf(value); trace_lbr_off(void) {
set_eflags_tf(0u);
} }

View File

@ -9,11 +9,9 @@
#ifndef TRACE_LBR_H_INCLUDED #ifndef TRACE_LBR_H_INCLUDED
#define TRACE_LBR_H_INCLUDED #define TRACE_LBR_H_INCLUDED
#include <inttypes.h> void trace_lbr_enable(void);
void trace_lbr_disable(void);
void trace_lbr_begin(void); void trace_lbr_on(void);
void trace_lbr_end(void); void trace_lbr_off(void);
uint32_t trace_lbr_pause(void);
void trace_lbr_resume(uint32_t value);
#endif #endif

View File

@ -638,7 +638,6 @@ proc set_eflags_tf c uses ebx esi edi ebp, tf
rol eax, 8 rol eax, 8
push eax push eax
popfd popfd
mov eax, edx
ret ret
endp endp

3
umka.h
View File

@ -862,6 +862,9 @@ umka_cli(void);
void void
umka_sti(void); umka_sti(void);
void
set_eflags_tf(uint32_t tf);
#define COVERAGE_TABLE_SIZE (512*1024) #define COVERAGE_TABLE_SIZE (512*1024)
struct coverage_branch { struct coverage_branch {

View File

@ -233,11 +233,9 @@ int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
(void)argc; (void)argc;
const char *usage = "umka_os [-i <infile>] [-o <outfile>]" const char *usage = "umka_os [-i <infile>] [-o <outfile>]"
" [-b <boardlog>] [-s <startupfile>]\n"; " [-b <boardlog>] [-s <startupfile>] [-c covfile]\n";
if (coverage) {
trace_begin();
}
int coverage = 0;
int show_display = 0; int show_display = 0;
umka_sti(); umka_sti();
@ -248,6 +246,7 @@ main(int argc, char *argv[]) {
const char *infile = NULL; const char *infile = NULL;
const char *outfile = NULL; const char *outfile = NULL;
const char *boardlogfile = NULL; const char *boardlogfile = NULL;
const char *covfile = NULL;
FILE *fstartup = NULL; FILE *fstartup = NULL;
FILE *fin = stdin; FILE *fin = stdin;
FILE *fout = stdout; FILE *fout = stdout;
@ -257,11 +256,15 @@ main(int argc, char *argv[]) {
int opt; int opt;
optparse_init(&options, argv); optparse_init(&options, argv);
while ((opt = optparse(&options, "b:di:o:s:")) != -1) { while ((opt = optparse(&options, "b:c:di:o:s:")) != -1) {
switch (opt) { switch (opt) {
case 'b': case 'b':
boardlogfile = options.optarg; boardlogfile = options.optarg;
break; break;
case 'c':
coverage = 1;
covfile = options.optarg;
break;
case 'd': case 'd':
show_display = 1; show_display = 1;
break; break;
@ -281,6 +284,10 @@ main(int argc, char *argv[]) {
} }
} }
if (coverage) {
trace_enable();
}
if (startupfile) { if (startupfile) {
fstartup = fopen(startupfile, "rb"); fstartup = fopen(startupfile, "rb");
if (!fstartup) { if (!fstartup) {
@ -429,7 +436,9 @@ main(int argc, char *argv[]) {
umka_osloop(); // doesn't return umka_osloop(); // doesn't return
if (coverage) if (coverage)
trace_end(); trace_disable();
(void)covfile;
return 0; return 0;
} }

View File

@ -63,7 +63,9 @@ main(int argc, char **argv) {
" -i infile file with commands\n" " -i infile file with commands\n"
" -o outfile file for logs\n" " -o outfile file for logs\n"
" -r reproducible logs (without pointers and datetime)\n" " -r reproducible logs (without pointers and datetime)\n"
" -c collect coverage\n"; " -c covfile collect coverage to the file\n";
char covfile[64];
const char *infile = NULL, *outfile = NULL; const char *infile = NULL, *outfile = NULL;
FILE *fin = stdin; FILE *fin = stdin;
@ -75,13 +77,14 @@ main(int argc, char **argv) {
kos_boot.memmap_blocks[1] = (e820entry_t){(uintptr_t)mem1, 128*1024*1024, 1}; kos_boot.memmap_blocks[1] = (e820entry_t){(uintptr_t)mem1, 128*1024*1024, 1};
kos_boot.memmap_blocks[2] = (e820entry_t){(uintptr_t)mem2, 256*1024*1024, 1}; kos_boot.memmap_blocks[2] = (e820entry_t){(uintptr_t)mem2, 256*1024*1024, 1};
*/ */
int coverage = 0;
int reproducible = 0; int reproducible = 0;
struct optparse options; struct optparse options;
optparse_init(&options, argv); optparse_init(&options, argv);
int opt; int opt;
while ((opt = optparse(&options, "i:o:rc")) != -1) { while ((opt = optparse(&options, "i:o:rc:")) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
infile = options.optarg; infile = options.optarg;
@ -94,6 +97,7 @@ main(int argc, char **argv) {
break; break;
case 'c': case 'c':
coverage = 1; coverage = 1;
sprintf(covfile, "%s.%i", options.optarg, getpid());
break; break;
case 'h': case 'h':
fputs(usage, stderr); fputs(usage, stderr);
@ -123,15 +127,13 @@ main(int argc, char **argv) {
struct umka_shell_ctx *ctx = umka_shell_init(reproducible, fin); struct umka_shell_ctx *ctx = umka_shell_init(reproducible, fin);
if (coverage) if (coverage)
trace_begin(); trace_enable();
run_test(ctx->shell); run_test(ctx->shell);
if (coverage) { if (coverage) {
trace_end(); trace_disable();
char coverage_filename[32]; FILE *f = fopen(covfile, "w");
sprintf(coverage_filename, "coverage.%i", getpid());
FILE *f = fopen(coverage_filename, "w");
fwrite(coverage_table, fwrite(coverage_table,
COVERAGE_TABLE_SIZE * sizeof(struct coverage_branch), 1, f); COVERAGE_TABLE_SIZE * sizeof(struct coverage_branch), 1, f);
fclose(f); fclose(f);