implement test coverage
I have implemented the -ftest-coverage option. It works a bit different from the gcc version. It output .tcov text file which looks almost the same as a gcov file after a executable/so file is run. Add lib/tcov.c file Modify Makefiles to compile/install it Add -ftest-coverage option in tcc.c/tcc.h/tcc-doc.texi Add code to tccelf.c/tccgen.c/tccpe.c Add gen_increment_tcov to tcc.h/*gen.c unrelated changes: Add sigemptyset in tccrun.c Fix riscv64-gen.c tok_alloc label size
This commit is contained in:
parent
66de1550ab
commit
bc6c0c34c1
16 changed files with 721 additions and 15 deletions
5
Makefile
5
Makefile
|
@ -336,11 +336,12 @@ IR = $(IM) mkdir -p $2 && cp -r $1/. $2
|
||||||
IM = $(info -> $2 : $1)@
|
IM = $(info -> $2 : $1)@
|
||||||
|
|
||||||
B_O = bcheck.o bt-exe.o bt-log.o bt-dll.o
|
B_O = bcheck.o bt-exe.o bt-log.o bt-dll.o
|
||||||
|
T_O = tcov.o
|
||||||
|
|
||||||
# install progs & libs
|
# install progs & libs
|
||||||
install-unx:
|
install-unx:
|
||||||
$(call IBw,$(PROGS) $(PROGS_CROSS),"$(bindir)")
|
$(call IBw,$(PROGS) $(PROGS_CROSS),"$(bindir)")
|
||||||
$(call IFw,$(LIBTCC1) $(B_O) $(LIBTCC1_U),"$(tccdir)")
|
$(call IFw,$(LIBTCC1) $(B_O) $(T_O) $(LIBTCC1_U),"$(tccdir)")
|
||||||
$(call IF,$(TOPSRC)/include/*.h $(TOPSRC)/tcclib.h,"$(tccdir)/include")
|
$(call IF,$(TOPSRC)/include/*.h $(TOPSRC)/tcclib.h,"$(tccdir)/include")
|
||||||
$(call $(if $(findstring .so,$(LIBTCC)),IBw,IFw),$(LIBTCC),"$(libdir)")
|
$(call $(if $(findstring .so,$(LIBTCC)),IBw,IFw),$(LIBTCC),"$(libdir)")
|
||||||
$(call IF,$(TOPSRC)/libtcc.h,"$(includedir)")
|
$(call IF,$(TOPSRC)/libtcc.h,"$(includedir)")
|
||||||
|
@ -365,7 +366,7 @@ uninstall-unx:
|
||||||
install-win:
|
install-win:
|
||||||
$(call IBw,$(PROGS) $(PROGS_CROSS) $(subst libtcc.a,,$(LIBTCC)),"$(bindir)")
|
$(call IBw,$(PROGS) $(PROGS_CROSS) $(subst libtcc.a,,$(LIBTCC)),"$(bindir)")
|
||||||
$(call IF,$(TOPSRC)/win32/lib/*.def,"$(tccdir)/lib")
|
$(call IF,$(TOPSRC)/win32/lib/*.def,"$(tccdir)/lib")
|
||||||
$(call IFw,libtcc1.a $(B_O) $(LIBTCC1_W),"$(tccdir)/lib")
|
$(call IFw,libtcc1.a $(B_O) $(T_O) $(LIBTCC1_W),"$(tccdir)/lib")
|
||||||
$(call IF,$(TOPSRC)/include/*.h $(TOPSRC)/tcclib.h,"$(tccdir)/include")
|
$(call IF,$(TOPSRC)/include/*.h $(TOPSRC)/tcclib.h,"$(tccdir)/include")
|
||||||
$(call IR,$(TOPSRC)/win32/include,"$(tccdir)/include")
|
$(call IR,$(TOPSRC)/win32/include,"$(tccdir)/include")
|
||||||
$(call IR,$(TOPSRC)/win32/examples,"$(tccdir)/examples")
|
$(call IR,$(TOPSRC)/win32/examples,"$(tccdir)/examples")
|
||||||
|
|
23
arm-gen.c
23
arm-gen.c
|
@ -2277,6 +2277,29 @@ void gen_cvt_ftof(int t)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* increment tcov counter */
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv)
|
||||||
|
{
|
||||||
|
int r1, r2;
|
||||||
|
|
||||||
|
vpushv(sv);
|
||||||
|
vtop->r = r1 = get_reg(RC_INT);
|
||||||
|
r2 = get_reg(RC_INT);
|
||||||
|
o(0xE59F0000 | (intr(r1)<<12)); // ldr r1,[pc]
|
||||||
|
o(0xEA000000); // b $+4
|
||||||
|
greloc(cur_text_section, sv->sym, ind, R_ARM_REL32);
|
||||||
|
o(-12);
|
||||||
|
o(0xe080000f | (intr(r1)<<16) | (intr(r1)<<12)); // add r1,r1,pc
|
||||||
|
o(0xe5900000 | (intr(r1)<<16) | (intr(r2)<<12)); // ldr r2, [r1]
|
||||||
|
o(0xe2900001 | (intr(r2)<<16) | (intr(r2)<<12)); // adds r2, r2, #1
|
||||||
|
o(0xe5800000 | (intr(r1)<<16) | (intr(r2)<<12)); // str r2, [r1]
|
||||||
|
o(0xe2800004 | (intr(r1)<<16) | (intr(r1)<<12)); // add r1, r1, #4
|
||||||
|
o(0xe5900000 | (intr(r1)<<16) | (intr(r2)<<12)); // ldr r2, [r1]
|
||||||
|
o(0xe2a00000 | (intr(r2)<<16) | (intr(r2)<<12)); // adc r2, r2, #0
|
||||||
|
o(0xe5800000 | (intr(r1)<<16) | (intr(r2)<<12)); // str r2, [r1]
|
||||||
|
vpop();
|
||||||
|
}
|
||||||
|
|
||||||
/* computed goto support */
|
/* computed goto support */
|
||||||
void ggoto(void)
|
void ggoto(void)
|
||||||
{
|
{
|
||||||
|
|
18
arm64-gen.c
18
arm64-gen.c
|
@ -1982,6 +1982,24 @@ ST_FUNC void gen_cvt_ftof(int t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* increment tcov counter */
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv)
|
||||||
|
{
|
||||||
|
int r1, r2;
|
||||||
|
|
||||||
|
vpushv(sv);
|
||||||
|
vtop->r = r1 = get_reg(RC_INT);
|
||||||
|
r2 = get_reg(RC_INT);
|
||||||
|
greloca(cur_text_section, sv->sym, ind, R_AARCH64_ADR_GOT_PAGE, 0);
|
||||||
|
o(0x90000000 | r1); // adrp r1, #sym
|
||||||
|
greloca(cur_text_section, sv->sym, ind, R_AARCH64_LD64_GOT_LO12_NC, 0);
|
||||||
|
o(0xf9400000 | r1 | (r1 << 5)); // ld xr,[xr, #sym]
|
||||||
|
o(0xf9400000 | (intr(r1)<<5) | intr(r2)); // ldr r2, [r1]
|
||||||
|
o(0x91000400 | (intr(r2)<<5) | intr(r2)); // add r2, r2, #1
|
||||||
|
o(0xf9000000 | (intr(r1)<<5) | intr(r2)); // str r2, [r1]
|
||||||
|
vpop();
|
||||||
|
}
|
||||||
|
|
||||||
ST_FUNC void ggoto(void)
|
ST_FUNC void ggoto(void)
|
||||||
{
|
{
|
||||||
arm64_gen_bl_or_b(1);
|
arm64_gen_bl_or_b(1);
|
||||||
|
|
13
i386-gen.c
13
i386-gen.c
|
@ -1023,6 +1023,19 @@ ST_FUNC void gen_cvt_csti(int t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* increment tcov counter */
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv)
|
||||||
|
{
|
||||||
|
o(0x0583); /* addl $1, xxx */
|
||||||
|
greloc(cur_text_section, sv->sym, ind, R_386_32);
|
||||||
|
gen_le32(0);
|
||||||
|
o(1);
|
||||||
|
o(0x1583); /* addcl $0, xxx */
|
||||||
|
greloc(cur_text_section, sv->sym, ind, R_386_32);
|
||||||
|
gen_le32(4);
|
||||||
|
g(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* computed goto support */
|
/* computed goto support */
|
||||||
ST_FUNC void ggoto(void)
|
ST_FUNC void ggoto(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -64,6 +64,7 @@ OBJ-arm-wince = $(ARM_O) $(WIN_O)
|
||||||
OBJ-riscv64 = $(RISCV64_O) $(BCHECK_O) $(DSO_O)
|
OBJ-riscv64 = $(RISCV64_O) $(BCHECK_O) $(DSO_O)
|
||||||
|
|
||||||
OBJ-extra = $(filter $(B_O),$(OBJ-$T))
|
OBJ-extra = $(filter $(B_O),$(OBJ-$T))
|
||||||
|
OBJ-extra += tcov.o
|
||||||
OBJ-libtcc1 = $(addprefix $(X),$(filter-out $(OBJ-extra),$(OBJ-$T)))
|
OBJ-libtcc1 = $(addprefix $(X),$(filter-out $(OBJ-extra),$(OBJ-$T)))
|
||||||
|
|
||||||
ALL = $(addprefix $(TOP)/,$(X)libtcc1.a $(OBJ-extra))
|
ALL = $(addprefix $(TOP)/,$(X)libtcc1.a $(OBJ-extra))
|
||||||
|
|
391
lib/tcov.c
Normal file
391
lib/tcov.c
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* section layout (all little endian):
|
||||||
|
32bit offset to executable/so file name
|
||||||
|
filename \0
|
||||||
|
function name \0
|
||||||
|
align to 64 bits
|
||||||
|
64bit function start line
|
||||||
|
64bits end_line(28bits) / start_line(28bits) / flag=0xff(8bits)
|
||||||
|
64bits counter
|
||||||
|
\0
|
||||||
|
\0
|
||||||
|
\0
|
||||||
|
executable/so file name \0
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct tcov_line {
|
||||||
|
unsigned int fline;
|
||||||
|
unsigned int lline;
|
||||||
|
unsigned long long count;
|
||||||
|
} tcov_line;
|
||||||
|
|
||||||
|
typedef struct tcov_function {
|
||||||
|
char *function;
|
||||||
|
unsigned int first_line;
|
||||||
|
unsigned int n_line;
|
||||||
|
unsigned int m_line;
|
||||||
|
tcov_line *line;
|
||||||
|
} tcov_function;
|
||||||
|
|
||||||
|
typedef struct tcov_file {
|
||||||
|
char *filename;
|
||||||
|
unsigned int n_func;
|
||||||
|
unsigned int m_func;
|
||||||
|
tcov_function *func;
|
||||||
|
struct tcov_file *next;
|
||||||
|
} tcov_file;
|
||||||
|
|
||||||
|
static unsigned long long get_value(unsigned char *p, int size)
|
||||||
|
{
|
||||||
|
unsigned long long value = 0;
|
||||||
|
|
||||||
|
p += size;
|
||||||
|
while (size--)
|
||||||
|
value = (value << 8) | *--p;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sort_func (const void *p, const void *q)
|
||||||
|
{
|
||||||
|
const tcov_function *pp = (const tcov_function *) p;
|
||||||
|
const tcov_function *pq = (const tcov_function *) q;
|
||||||
|
|
||||||
|
return pp->first_line > pq->first_line ? 1 :
|
||||||
|
pp->first_line < pq->first_line ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sort_line (const void *p, const void *q)
|
||||||
|
{
|
||||||
|
const tcov_line *pp = (const tcov_line *) p;
|
||||||
|
const tcov_line *pq = (const tcov_line *) q;
|
||||||
|
|
||||||
|
return pp->fline > pq->fline ? 1 :
|
||||||
|
pp->fline < pq->fline ? -1 :
|
||||||
|
pp->count < pq->count ? 1 :
|
||||||
|
pp->count > pq->count ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort to let inline functions work */
|
||||||
|
static tcov_file *sort_test_coverage (unsigned char *p)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
tcov_file *file = NULL;
|
||||||
|
tcov_file *nfile;
|
||||||
|
|
||||||
|
p += 4;
|
||||||
|
while (*p) {
|
||||||
|
char *filename = (char *)p;
|
||||||
|
size_t len = strlen (filename);
|
||||||
|
|
||||||
|
nfile = file;
|
||||||
|
while (nfile) {
|
||||||
|
if (strcmp (nfile->filename, filename) == 0)
|
||||||
|
break;
|
||||||
|
nfile = nfile->next;
|
||||||
|
}
|
||||||
|
if (nfile == NULL) {
|
||||||
|
nfile = malloc (sizeof(tcov_file));
|
||||||
|
if (nfile == NULL) {
|
||||||
|
fprintf (stderr, "Malloc error test_coverage\n");
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
nfile->filename = filename;
|
||||||
|
nfile->n_func = 0;
|
||||||
|
nfile->m_func = 0;
|
||||||
|
nfile->func = NULL;
|
||||||
|
nfile->next = NULL;
|
||||||
|
if (file == NULL)
|
||||||
|
file = nfile;
|
||||||
|
else {
|
||||||
|
tcov_file *lfile = file;
|
||||||
|
|
||||||
|
while (lfile->next)
|
||||||
|
lfile = lfile->next;
|
||||||
|
lfile->next = nfile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p += len + 1;
|
||||||
|
while (*p) {
|
||||||
|
int i;
|
||||||
|
char *function = (char *)p;
|
||||||
|
tcov_function *func;
|
||||||
|
|
||||||
|
p += strlen (function) + 1;
|
||||||
|
p += -(size_t)p & 7;
|
||||||
|
for (i = 0; i < nfile->n_func; i++) {
|
||||||
|
func = &nfile->func[i];
|
||||||
|
if (strcmp (func->function, function) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == nfile->n_func) {
|
||||||
|
if (nfile->n_func >= nfile->m_func) {
|
||||||
|
nfile->m_func = nfile->m_func == 0 ? 4 : nfile->m_func * 2;
|
||||||
|
nfile->func = realloc (nfile->func,
|
||||||
|
nfile->m_func *
|
||||||
|
sizeof (tcov_function));
|
||||||
|
if (nfile->func == NULL) {
|
||||||
|
fprintf (stderr, "Realloc error test_coverage\n");
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func = &nfile->func[nfile->n_func++];
|
||||||
|
func->function = function;
|
||||||
|
func->first_line = get_value (p, 8);
|
||||||
|
func->n_line = 0;
|
||||||
|
func->m_line = 0;
|
||||||
|
func->line = NULL;
|
||||||
|
}
|
||||||
|
p += 8;
|
||||||
|
while (*p) {
|
||||||
|
tcov_line *line;
|
||||||
|
unsigned long long val;
|
||||||
|
|
||||||
|
if (func->n_line >= func->m_line) {
|
||||||
|
func->m_line = func->m_line == 0 ? 4 : func->m_line * 2;
|
||||||
|
func->line = realloc (func->line,
|
||||||
|
func->m_line * sizeof (tcov_line));
|
||||||
|
if (func->line == NULL) {
|
||||||
|
fprintf (stderr, "Realloc error test_coverage\n");
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line = &func->line[func->n_line++];
|
||||||
|
val = get_value (p, 8);
|
||||||
|
line->fline = (val >> 8) & 0xfffffffULL;
|
||||||
|
line->lline = val >> 36;
|
||||||
|
line->count = get_value (p + 8, 8);
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
nfile = file;
|
||||||
|
while (nfile) {
|
||||||
|
qsort (nfile->func, nfile->n_func, sizeof (tcov_function), sort_func);
|
||||||
|
for (i = 0; i < nfile->n_func; i++) {
|
||||||
|
tcov_function *func = &nfile->func[i];
|
||||||
|
qsort (func->line, func->n_line, sizeof (tcov_line), sort_line);
|
||||||
|
}
|
||||||
|
nfile = nfile->next;
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* merge with previous tcov file */
|
||||||
|
static void merge_test_coverage (tcov_file *file, char *cov_filename,
|
||||||
|
unsigned int *pruns)
|
||||||
|
{
|
||||||
|
unsigned int runs;
|
||||||
|
FILE *fp = fopen (cov_filename, "r");
|
||||||
|
char *p;
|
||||||
|
char str[10000];
|
||||||
|
|
||||||
|
*pruns = 1;
|
||||||
|
if (fp == NULL)
|
||||||
|
return;
|
||||||
|
if (fgets(str, sizeof(str), fp) &&
|
||||||
|
(p = strrchr (str, ':')) &&
|
||||||
|
(sscanf (p + 1, "%u", &runs) == 1))
|
||||||
|
*pruns = runs + 1;
|
||||||
|
while (file) {
|
||||||
|
int i;
|
||||||
|
size_t len = strlen (file->filename);
|
||||||
|
|
||||||
|
while (fgets(str, sizeof(str), fp) &&
|
||||||
|
(p = strstr(str, "0:File:")) == NULL);
|
||||||
|
if ((p = strstr(str, "0:File:")) == NULL ||
|
||||||
|
strncmp (p + strlen("0:File:"), file->filename, len) != 0 ||
|
||||||
|
p[strlen("0:File:") + len] != ' ')
|
||||||
|
break;
|
||||||
|
for (i = 0; i < file->n_func; i++) {
|
||||||
|
int j;
|
||||||
|
tcov_function *func = &file->func[i];
|
||||||
|
unsigned int next_zero = 0;
|
||||||
|
unsigned int curline = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < func->n_line; j++) {
|
||||||
|
tcov_line *line = &func->line[j];
|
||||||
|
unsigned int fline = line->fline;
|
||||||
|
unsigned long long count;
|
||||||
|
unsigned int tmp;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
while (curline < fline &&
|
||||||
|
fgets(str, sizeof(str), fp))
|
||||||
|
if ((p = strchr(str, ':')) &&
|
||||||
|
sscanf (p + 1, "%u", &tmp) == 1)
|
||||||
|
curline = tmp;
|
||||||
|
if (sscanf (str, "%llu%c\n", &count, &c) == 2) {
|
||||||
|
if (next_zero == 0)
|
||||||
|
line->count += count;
|
||||||
|
next_zero = c == '*';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file = file->next;
|
||||||
|
}
|
||||||
|
fclose (fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store tcov data in file */
|
||||||
|
void __store_test_coverage (unsigned char * p)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
unsigned int files;
|
||||||
|
unsigned int funcs;
|
||||||
|
unsigned int blocks;
|
||||||
|
unsigned int blocks_run;
|
||||||
|
unsigned int runs;
|
||||||
|
char *cov_filename = (char *)p + get_value (p, 4);
|
||||||
|
FILE *fp;
|
||||||
|
char *q;
|
||||||
|
tcov_file *file;
|
||||||
|
tcov_file *nfile;
|
||||||
|
tcov_function *func;
|
||||||
|
|
||||||
|
file = sort_test_coverage (p);
|
||||||
|
merge_test_coverage (file, cov_filename, &runs);
|
||||||
|
fp = fopen (cov_filename, "w");
|
||||||
|
if (fp == NULL) {
|
||||||
|
fprintf (stderr, "Cannot create coverage file: %s\n", cov_filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fprintf (fp, " -: 0:Runs:%u\n", runs);
|
||||||
|
files = 0;
|
||||||
|
funcs = 0;
|
||||||
|
blocks = 0;
|
||||||
|
blocks_run = 0;
|
||||||
|
nfile = file;
|
||||||
|
while (nfile) {
|
||||||
|
files++;
|
||||||
|
for (i = 0; i < nfile->n_func; i++) {
|
||||||
|
func = &nfile->func[i];
|
||||||
|
funcs++;
|
||||||
|
for (j = 0; j < func->n_line; j++) {
|
||||||
|
blocks++;
|
||||||
|
blocks_run += func->line[j].count != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nfile = nfile->next;
|
||||||
|
}
|
||||||
|
if (blocks == 0)
|
||||||
|
blocks = 1;
|
||||||
|
fprintf (fp, " -: 0:All:%s Files:%u Functions:%u %.02f%%\n",
|
||||||
|
cov_filename, files, funcs, 100.0 * (double) blocks_run / blocks);
|
||||||
|
nfile = file;
|
||||||
|
while (nfile) {
|
||||||
|
FILE *src = fopen (nfile->filename, "r");
|
||||||
|
unsigned int curline = 1;
|
||||||
|
char str[10000];
|
||||||
|
|
||||||
|
if (src == NULL)
|
||||||
|
goto next;
|
||||||
|
funcs = 0;
|
||||||
|
blocks = 0;
|
||||||
|
blocks_run = 0;
|
||||||
|
for (i = 0; i < nfile->n_func; i++) {
|
||||||
|
func = &nfile->func[i];
|
||||||
|
funcs++;
|
||||||
|
for (j = 0; j < func->n_line; j++) {
|
||||||
|
blocks++;
|
||||||
|
blocks_run += func->line[j].count != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (blocks == 0)
|
||||||
|
blocks = 1;
|
||||||
|
fprintf (fp, " -: 0:File:%s Functions:%u %.02f%%\n",
|
||||||
|
nfile->filename, funcs, 100.0 * (double) blocks_run / blocks);
|
||||||
|
for (i = 0; i < nfile->n_func; i++) {
|
||||||
|
func = &nfile->func[i];
|
||||||
|
|
||||||
|
while (curline < func->first_line)
|
||||||
|
if (fgets(str, sizeof(str), src))
|
||||||
|
fprintf (fp, " -:%5u:%s", curline++, str);
|
||||||
|
blocks = 0;
|
||||||
|
blocks_run = 0;
|
||||||
|
for (j = 0; j < func->n_line; j++) {
|
||||||
|
blocks++;
|
||||||
|
blocks_run += func->line[j].count != 0;
|
||||||
|
}
|
||||||
|
if (blocks == 0)
|
||||||
|
blocks = 1;
|
||||||
|
fprintf (fp, " -: 0:Function:%s %.02f%%\n",
|
||||||
|
func->function, 100.0 * (double) blocks_run / blocks);
|
||||||
|
#if 0
|
||||||
|
for (j = 0; j < func->n_line; j++) {
|
||||||
|
unsigned int fline = func->line[j].fline;
|
||||||
|
unsigned int lline = func->line[j].lline;
|
||||||
|
unsigned long long count = func->line[j].count;
|
||||||
|
|
||||||
|
fprintf (fp, "%u %u %llu\n", fline, lline, count);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (j = 0; j < func->n_line;) {
|
||||||
|
unsigned int fline = func->line[j].fline;
|
||||||
|
unsigned int lline = func->line[j].lline;
|
||||||
|
unsigned long long count = func->line[j].count;
|
||||||
|
unsigned int has_zero = 0;
|
||||||
|
unsigned int same_line = fline == lline;
|
||||||
|
|
||||||
|
j++;
|
||||||
|
while (j < func->n_line) {
|
||||||
|
unsigned int nfline = func->line[j].fline;
|
||||||
|
unsigned int nlline = func->line[j].lline;
|
||||||
|
unsigned long long ncount = func->line[j].count;
|
||||||
|
|
||||||
|
if (fline == nfline) {
|
||||||
|
if (ncount == 0)
|
||||||
|
has_zero = 1;
|
||||||
|
else if (ncount > count)
|
||||||
|
count = ncount;
|
||||||
|
same_line = nfline == nlline;
|
||||||
|
lline = nlline;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (same_line)
|
||||||
|
lline++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (curline < fline)
|
||||||
|
if (fgets(str, sizeof(str), src))
|
||||||
|
fprintf (fp, " -:%5u:%s", curline++, str);
|
||||||
|
while (curline < lline &&
|
||||||
|
fgets(str, sizeof(str), src)) {
|
||||||
|
if (count == 0)
|
||||||
|
fprintf (fp, " #####:%5u:%s",
|
||||||
|
curline, str);
|
||||||
|
else if (has_zero)
|
||||||
|
fprintf (fp, "%8llu*:%5u:%s",
|
||||||
|
count, curline, str);
|
||||||
|
else
|
||||||
|
fprintf (fp, "%9llu:%5u:%s",
|
||||||
|
count, curline, str);
|
||||||
|
curline++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (fgets(str, sizeof(str), src))
|
||||||
|
fprintf (fp, " -:%5u:%s", curline++, str);
|
||||||
|
fclose (src);
|
||||||
|
next:
|
||||||
|
nfile = nfile->next;
|
||||||
|
}
|
||||||
|
while (file) {
|
||||||
|
for (i = 0; i < file->n_func; i++) {
|
||||||
|
func = &file->func[i];
|
||||||
|
free (func->line);
|
||||||
|
}
|
||||||
|
free (file->func);
|
||||||
|
nfile = file;
|
||||||
|
file = file->next;
|
||||||
|
free (nfile);
|
||||||
|
}
|
||||||
|
fclose (fp);
|
||||||
|
}
|
1
libtcc.c
1
libtcc.c
|
@ -1645,6 +1645,7 @@ static const FlagDef options_f[] = {
|
||||||
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
|
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
|
||||||
{ offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
|
{ offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
|
||||||
{ offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
|
{ offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
|
||||||
|
{ offsetof(TCCState, test_coverage), 0, "test-coverage" },
|
||||||
{ 0, 0, NULL }
|
{ 0, 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -476,7 +476,7 @@ static void gen_bounds_epilog(void)
|
||||||
func_bound_offset, lbounds_section->data_offset);
|
func_bound_offset, lbounds_section->data_offset);
|
||||||
|
|
||||||
if (!label.v) {
|
if (!label.v) {
|
||||||
label.v = tok_alloc(".LB0 ", 4)->tok;
|
label.v = tok_alloc(".LB0 ", 5)->tok;
|
||||||
label.type.t = VT_VOID | VT_STATIC;
|
label.type.t = VT_VOID | VT_STATIC;
|
||||||
}
|
}
|
||||||
/* generate bound local allocation */
|
/* generate bound local allocation */
|
||||||
|
@ -1348,6 +1348,37 @@ ST_FUNC void gen_cvt_ftof(int dt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* increment tcov counter */
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv)
|
||||||
|
{
|
||||||
|
int r1, r2;
|
||||||
|
static Sym label;
|
||||||
|
|
||||||
|
if (!label.v) {
|
||||||
|
label.v = tok_alloc(".T0 ", 4)->tok;
|
||||||
|
label.type.t = VT_VOID | VT_STATIC;
|
||||||
|
}
|
||||||
|
vpushv(sv);
|
||||||
|
vtop->r = r1 = get_reg(RC_INT);
|
||||||
|
r2 = get_reg(RC_INT);
|
||||||
|
r1 = ireg(r1);
|
||||||
|
r2 = ireg(r2);
|
||||||
|
greloca(cur_text_section, sv->sym, ind, R_RISCV_PCREL_HI20, 0);
|
||||||
|
label.c = 0; /* force new local ELF symbol */
|
||||||
|
put_extern_sym(&label, cur_text_section, ind, 0);
|
||||||
|
o(0x17 | (r1 << 7)); // auipc RR, 0 %pcrel_hi(sym)
|
||||||
|
greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_I, 0);
|
||||||
|
EI(0x03, 3, r2, r1, 0); // ld r2, x[r1]
|
||||||
|
EI(0x13, 0, r2, r2, 1); // addi r2, r2, #1
|
||||||
|
greloca(cur_text_section, sv->sym, ind, R_RISCV_PCREL_HI20, 0);
|
||||||
|
label.c = 0; /* force new local ELF symbol */
|
||||||
|
put_extern_sym(&label, cur_text_section, ind, 0);
|
||||||
|
o(0x17 | (r1 << 7)); // auipc RR, 0 %pcrel_hi(sym)
|
||||||
|
greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_S, 0);
|
||||||
|
ES(0x23, 3, r1, r2, 0); // sd r2, [r1]
|
||||||
|
vpop();
|
||||||
|
}
|
||||||
|
|
||||||
ST_FUNC void ggoto(void)
|
ST_FUNC void ggoto(void)
|
||||||
{
|
{
|
||||||
gcall_or_jmp(0);
|
gcall_or_jmp(0);
|
||||||
|
|
|
@ -238,6 +238,10 @@ behaves like an unnamed one.
|
||||||
@item -fdollars-in-identifiers
|
@item -fdollars-in-identifiers
|
||||||
Allow dollar signs in identifiers
|
Allow dollar signs in identifiers
|
||||||
|
|
||||||
|
@item -ftest-coverage
|
||||||
|
Create code coverage code. After running the resulting code an executable.tcov
|
||||||
|
or sofile.tcov file is generated with code coverage.
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
Warning options:
|
Warning options:
|
||||||
|
|
1
tcc.c
1
tcc.c
|
@ -111,6 +111,7 @@ static const char help2[] =
|
||||||
" leading-underscore decorate extern symbols\n"
|
" leading-underscore decorate extern symbols\n"
|
||||||
" ms-extensions allow anonymous struct in struct\n"
|
" ms-extensions allow anonymous struct in struct\n"
|
||||||
" dollars-in-identifiers allow '$' in C symbols\n"
|
" dollars-in-identifiers allow '$' in C symbols\n"
|
||||||
|
" test-coverage create code coverage code\n"
|
||||||
"-m... target specific options:\n"
|
"-m... target specific options:\n"
|
||||||
" ms-bitfields use MSVC bitfield layout\n"
|
" ms-bitfields use MSVC bitfield layout\n"
|
||||||
#ifdef TCC_TARGET_ARM
|
#ifdef TCC_TARGET_ARM
|
||||||
|
|
8
tcc.h
8
tcc.h
|
@ -764,6 +764,7 @@ struct TCCState {
|
||||||
unsigned char leading_underscore;
|
unsigned char leading_underscore;
|
||||||
unsigned char ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */
|
unsigned char ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */
|
||||||
unsigned char dollars_in_identifiers; /* allows '$' char in identifiers */
|
unsigned char dollars_in_identifiers; /* allows '$' char in identifiers */
|
||||||
|
unsigned char test_coverage; /* generate test coverage code */
|
||||||
unsigned char ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */
|
unsigned char ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */
|
||||||
|
|
||||||
/* warning switches */
|
/* warning switches */
|
||||||
|
@ -894,6 +895,8 @@ struct TCCState {
|
||||||
Section *bounds_section; /* contains global data bound description */
|
Section *bounds_section; /* contains global data bound description */
|
||||||
Section *lbounds_section; /* contains local data bound description */
|
Section *lbounds_section; /* contains local data bound description */
|
||||||
#endif
|
#endif
|
||||||
|
/* test coverage */
|
||||||
|
Section *tcov_section;
|
||||||
/* symbol sections */
|
/* symbol sections */
|
||||||
Section *symtab_section;
|
Section *symtab_section;
|
||||||
/* debug sections */
|
/* debug sections */
|
||||||
|
@ -1700,6 +1703,7 @@ ST_FUNC void gen_le32(int c);
|
||||||
ST_FUNC void gen_addr32(int r, Sym *sym, int c);
|
ST_FUNC void gen_addr32(int r, Sym *sym, int c);
|
||||||
ST_FUNC void gen_addrpc32(int r, Sym *sym, int c);
|
ST_FUNC void gen_addrpc32(int r, Sym *sym, int c);
|
||||||
ST_FUNC void gen_cvt_csti(int t);
|
ST_FUNC void gen_cvt_csti(int t);
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ------------ x86_64-gen.c ------------ */
|
/* ------------ x86_64-gen.c ------------ */
|
||||||
|
@ -1719,6 +1723,7 @@ ST_FUNC void gen_cvt_csti(int t);
|
||||||
PUB_FUNC const char *default_elfinterp(struct TCCState *s);
|
PUB_FUNC const char *default_elfinterp(struct TCCState *s);
|
||||||
#endif
|
#endif
|
||||||
ST_FUNC void arm_init(struct TCCState *s);
|
ST_FUNC void arm_init(struct TCCState *s);
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ------------ arm64-gen.c ------------ */
|
/* ------------ arm64-gen.c ------------ */
|
||||||
|
@ -1730,6 +1735,7 @@ ST_FUNC void gen_va_arg(CType *t);
|
||||||
ST_FUNC void gen_clear_cache(void);
|
ST_FUNC void gen_clear_cache(void);
|
||||||
ST_FUNC void gen_cvt_sxtw(void);
|
ST_FUNC void gen_cvt_sxtw(void);
|
||||||
ST_FUNC void gen_cvt_csti(int t);
|
ST_FUNC void gen_cvt_csti(int t);
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ------------ riscv64-gen.c ------------ */
|
/* ------------ riscv64-gen.c ------------ */
|
||||||
|
@ -1739,6 +1745,7 @@ ST_FUNC void gen_opl(int op);
|
||||||
ST_FUNC void gen_va_start(void);
|
ST_FUNC void gen_va_start(void);
|
||||||
ST_FUNC void arch_transfer_ret_regs(int);
|
ST_FUNC void arch_transfer_ret_regs(int);
|
||||||
ST_FUNC void gen_cvt_sxtw(void);
|
ST_FUNC void gen_cvt_sxtw(void);
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ------------ c67-gen.c ------------ */
|
/* ------------ c67-gen.c ------------ */
|
||||||
|
@ -1841,6 +1848,7 @@ ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename)
|
||||||
#define cur_text_section TCC_STATE_VAR(cur_text_section)
|
#define cur_text_section TCC_STATE_VAR(cur_text_section)
|
||||||
#define bounds_section TCC_STATE_VAR(bounds_section)
|
#define bounds_section TCC_STATE_VAR(bounds_section)
|
||||||
#define lbounds_section TCC_STATE_VAR(lbounds_section)
|
#define lbounds_section TCC_STATE_VAR(lbounds_section)
|
||||||
|
#define tcov_section TCC_STATE_VAR(tcov_section)
|
||||||
#define symtab_section TCC_STATE_VAR(symtab_section)
|
#define symtab_section TCC_STATE_VAR(symtab_section)
|
||||||
#define stab_section TCC_STATE_VAR(stab_section)
|
#define stab_section TCC_STATE_VAR(stab_section)
|
||||||
#define stabstr_section stab_section->link
|
#define stabstr_section stab_section->link
|
||||||
|
|
65
tccelf.c
65
tccelf.c
|
@ -1354,16 +1354,6 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_TCC_BACKTRACE
|
|
||||||
static void put_ptr(TCCState *s1, Section *s, int offs)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
c = set_global_sym(s1, NULL, s, offs);
|
|
||||||
s = data_section;
|
|
||||||
put_elf_reloc (s1->symtab, s, s->data_offset, R_DATA_PTR, c);
|
|
||||||
section_ptr_add(s, PTR_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set symbol to STB_LOCAL and resolve. The point is to not export it as
|
/* set symbol to STB_LOCAL and resolve. The point is to not export it as
|
||||||
a dynamic symbol to allow so's to have one each with a different value. */
|
a dynamic symbol to allow so's to have one each with a different value. */
|
||||||
static void set_local_sym(TCCState *s1, const char *name, Section *s, int offset)
|
static void set_local_sym(TCCState *s1, const char *name, Section *s, int offset)
|
||||||
|
@ -1377,6 +1367,16 @@ static void set_local_sym(TCCState *s1, const char *name, Section *s, int offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_TCC_BACKTRACE
|
||||||
|
static void put_ptr(TCCState *s1, Section *s, int offs)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
c = set_global_sym(s1, NULL, s, offs);
|
||||||
|
s = data_section;
|
||||||
|
put_elf_reloc (s1->symtab, s, s->data_offset, R_DATA_PTR, c);
|
||||||
|
section_ptr_add(s, PTR_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
ST_FUNC void tcc_add_btstub(TCCState *s1)
|
ST_FUNC void tcc_add_btstub(TCCState *s1)
|
||||||
{
|
{
|
||||||
Section *s;
|
Section *s;
|
||||||
|
@ -1427,6 +1427,45 @@ ST_FUNC void tcc_add_btstub(TCCState *s1)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void tcc_tcov_add_file(TCCState *s1, const char *filename)
|
||||||
|
{
|
||||||
|
CString cstr;
|
||||||
|
void *ptr;
|
||||||
|
char wd[1024];
|
||||||
|
|
||||||
|
if (tcov_section == NULL)
|
||||||
|
return;
|
||||||
|
section_ptr_add(tcov_section, 1);
|
||||||
|
write32le (tcov_section->data, tcov_section->data_offset);
|
||||||
|
|
||||||
|
getcwd (wd, sizeof(wd));
|
||||||
|
cstr_new (&cstr);
|
||||||
|
cstr_printf (&cstr, "%s/%s.tcov", wd, filename);
|
||||||
|
ptr = section_ptr_add(tcov_section, cstr.size + 1);
|
||||||
|
strncpy((char *)ptr, cstr.data, cstr.size);
|
||||||
|
unlink((char *)ptr);
|
||||||
|
#ifdef _WIN32
|
||||||
|
normalize_slashes((char *)ptr);
|
||||||
|
#endif
|
||||||
|
cstr_free (&cstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcc_add_tcov(TCCState *s1)
|
||||||
|
{
|
||||||
|
CString cstr;
|
||||||
|
|
||||||
|
cstr_new(&cstr);
|
||||||
|
cstr_printf(&cstr,
|
||||||
|
"extern char *__tcov_data[];"
|
||||||
|
"extern void __store_test_coverage ();"
|
||||||
|
"__attribute__((destructor)) static void __tcov_exit() {"
|
||||||
|
"__store_test_coverage(__tcov_data);"
|
||||||
|
"}");
|
||||||
|
tcc_compile_string(s1, cstr.data);
|
||||||
|
cstr_free(&cstr);
|
||||||
|
set_local_sym(s1, &"___tcov_data"[!s1->leading_underscore], tcov_section, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef TCC_TARGET_PE
|
#ifndef TCC_TARGET_PE
|
||||||
/* add tcc runtime libraries */
|
/* add tcc runtime libraries */
|
||||||
ST_FUNC void tcc_add_runtime(TCCState *s1)
|
ST_FUNC void tcc_add_runtime(TCCState *s1)
|
||||||
|
@ -1440,6 +1479,8 @@ ST_FUNC void tcc_add_runtime(TCCState *s1)
|
||||||
if (!s1->nostdlib) {
|
if (!s1->nostdlib) {
|
||||||
if (s1->option_pthread)
|
if (s1->option_pthread)
|
||||||
tcc_add_library_err(s1, "pthread");
|
tcc_add_library_err(s1, "pthread");
|
||||||
|
if (s1->test_coverage)
|
||||||
|
tcc_add_support(s1, "tcov.o");
|
||||||
tcc_add_library_err(s1, "c");
|
tcc_add_library_err(s1, "c");
|
||||||
#ifdef TCC_LIBGCC
|
#ifdef TCC_LIBGCC
|
||||||
if (!s1->static_link) {
|
if (!s1->static_link) {
|
||||||
|
@ -1473,6 +1514,8 @@ ST_FUNC void tcc_add_runtime(TCCState *s1)
|
||||||
tcc_add_btstub(s1);
|
tcc_add_btstub(s1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (s1->test_coverage)
|
||||||
|
tcc_add_tcov(s1);
|
||||||
if (strlen(TCC_LIBTCC1) > 0)
|
if (strlen(TCC_LIBTCC1) > 0)
|
||||||
tcc_add_support(s1, TCC_LIBTCC1);
|
tcc_add_support(s1, TCC_LIBTCC1);
|
||||||
#if TARGETOS_OpenBSD || TARGETOS_FreeBSD || TARGETOS_NetBSD
|
#if TARGETOS_OpenBSD || TARGETOS_FreeBSD || TARGETOS_NetBSD
|
||||||
|
@ -2772,6 +2815,8 @@ static int elf_output_obj(TCCState *s1, const char *filename)
|
||||||
|
|
||||||
LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
|
LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
|
||||||
{
|
{
|
||||||
|
if (s->test_coverage)
|
||||||
|
tcc_tcov_add_file(s, filename);
|
||||||
if (s->output_type == TCC_OUTPUT_OBJ)
|
if (s->output_type == TCC_OUTPUT_OBJ)
|
||||||
return elf_output_obj(s, filename);
|
return elf_output_obj(s, filename);
|
||||||
#ifdef TCC_TARGET_PE
|
#ifdef TCC_TARGET_PE
|
||||||
|
|
159
tccgen.c
159
tccgen.c
|
@ -51,6 +51,10 @@ ST_DATA SValue *vtop;
|
||||||
static SValue _vstack[1 + VSTACK_SIZE];
|
static SValue _vstack[1 + VSTACK_SIZE];
|
||||||
#define vstack (_vstack + 1)
|
#define vstack (_vstack + 1)
|
||||||
|
|
||||||
|
static void tcc_tcov_block_begin(void);
|
||||||
|
static void tcc_tcov_block_end(int line);
|
||||||
|
static void tcc_tcov_check_line(int start);
|
||||||
|
|
||||||
ST_DATA int const_wanted; /* true if constant wanted */
|
ST_DATA int const_wanted; /* true if constant wanted */
|
||||||
ST_DATA int nocode_wanted; /* no code generation wanted */
|
ST_DATA int nocode_wanted; /* no code generation wanted */
|
||||||
#define unevalmask 0xffff /* unevaluated subexpression */
|
#define unevalmask 0xffff /* unevaluated subexpression */
|
||||||
|
@ -63,7 +67,7 @@ ST_DATA int nocode_wanted; /* no code generation wanted */
|
||||||
|
|
||||||
/* Clear 'nocode_wanted' at label if it was used */
|
/* Clear 'nocode_wanted' at label if it was used */
|
||||||
ST_FUNC void gsym(int t) { if (t) { gsym_addr(t, ind); CODE_ON(); }}
|
ST_FUNC void gsym(int t) { if (t) { gsym_addr(t, ind); CODE_ON(); }}
|
||||||
static int gind(void) { CODE_ON(); return ind; }
|
static int gind(void) { int t; CODE_ON(); t = ind; tcc_tcov_block_begin(); return t; }
|
||||||
|
|
||||||
/* Set 'nocode_wanted' after unconditional jumps */
|
/* Set 'nocode_wanted' after unconditional jumps */
|
||||||
static void gjmp_addr_acs(int t) { gjmp_addr(t); CODE_OFF(); }
|
static void gjmp_addr_acs(int t) { gjmp_addr(t); CODE_OFF(); }
|
||||||
|
@ -203,6 +207,16 @@ static struct debug_info {
|
||||||
struct debug_info *child, *next, *last, *parent;
|
struct debug_info *child, *next, *last, *parent;
|
||||||
} *debug_info, *debug_info_root;
|
} *debug_info, *debug_info_root;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
unsigned long offset;
|
||||||
|
unsigned long last_offset;
|
||||||
|
unsigned long last_file_name;
|
||||||
|
unsigned long last_func_name;
|
||||||
|
int ind;
|
||||||
|
int line;
|
||||||
|
Sym label;
|
||||||
|
} tcov_data;
|
||||||
|
|
||||||
/********************************************************/
|
/********************************************************/
|
||||||
#if 1
|
#if 1
|
||||||
#define precedence_parser
|
#define precedence_parser
|
||||||
|
@ -700,6 +714,136 @@ ST_FUNC void tcc_debug_end(TCCState *s1)
|
||||||
tcc_free(debug_hash);
|
tcc_free(debug_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* for section layout see lib/tcov.c */
|
||||||
|
static void tcc_tcov_block_begin(void)
|
||||||
|
{
|
||||||
|
SValue sv;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
tcc_tcov_block_end (0);
|
||||||
|
if (tcc_state->test_coverage == 0 || nocode_wanted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (tcov_data.last_file_name == 0 ||
|
||||||
|
strcmp ((const char *)(tcov_section->data + tcov_data.last_file_name),
|
||||||
|
file->true_filename) != 0) {
|
||||||
|
char wd[1024];
|
||||||
|
CString cstr;
|
||||||
|
|
||||||
|
if (tcov_data.last_func_name)
|
||||||
|
section_ptr_add(tcov_section, 1);
|
||||||
|
if (tcov_data.last_file_name)
|
||||||
|
section_ptr_add(tcov_section, 1);
|
||||||
|
getcwd (wd, sizeof(wd));
|
||||||
|
tcov_data.last_file_name = tcov_section->data_offset + strlen(wd) + 1;
|
||||||
|
tcov_data.last_func_name = 0;
|
||||||
|
cstr_new (&cstr);
|
||||||
|
cstr_printf (&cstr, "%s/%s", wd, file->true_filename);
|
||||||
|
ptr = section_ptr_add(tcov_section, cstr.size + 1);
|
||||||
|
strncpy((char *)ptr, cstr.data, cstr.size);
|
||||||
|
#ifdef _WIN32
|
||||||
|
normalize_slashes((char *)ptr);
|
||||||
|
#endif
|
||||||
|
cstr_free (&cstr);
|
||||||
|
}
|
||||||
|
if (tcov_data.last_func_name == 0 ||
|
||||||
|
strcmp ((const char *)(tcov_section->data + tcov_data.last_func_name),
|
||||||
|
funcname) != 0) {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (tcov_data.last_func_name)
|
||||||
|
section_ptr_add(tcov_section, 1);
|
||||||
|
tcov_data.last_func_name = tcov_section->data_offset;
|
||||||
|
len = strlen (funcname);
|
||||||
|
ptr = section_ptr_add(tcov_section, len + 1);
|
||||||
|
strncpy((char *)ptr, funcname, len);
|
||||||
|
section_ptr_add(tcov_section, -tcov_section->data_offset & 7);
|
||||||
|
ptr = section_ptr_add(tcov_section, 8);
|
||||||
|
write64le (ptr, file->line_num);
|
||||||
|
}
|
||||||
|
if (ind == tcov_data.ind && tcov_data.line == file->line_num)
|
||||||
|
tcov_data.offset = tcov_data.last_offset;
|
||||||
|
else {
|
||||||
|
if (!tcov_data.label.v) {
|
||||||
|
tcov_data.label.v = tok_alloc(".TCOV ", 6)->tok;
|
||||||
|
tcov_data.label.type.t = VT_LLONG | VT_STATIC;
|
||||||
|
}
|
||||||
|
tcov_data.label.c = 0; /* force new local ELF symbol */
|
||||||
|
ptr = section_ptr_add(tcov_section, 16);
|
||||||
|
tcov_data.line = file->line_num;
|
||||||
|
write64le (ptr, (tcov_data.line << 8) | 0xff);
|
||||||
|
put_extern_sym(&tcov_data.label, tcov_section,
|
||||||
|
((unsigned char *)ptr - tcov_section->data) + 8, 0);
|
||||||
|
sv.type = tcov_data.label.type;
|
||||||
|
sv.r = VT_SYM | VT_LVAL | VT_CONST;
|
||||||
|
sv.r2 = VT_CONST;
|
||||||
|
sv.c.i = 0;
|
||||||
|
sv.sym = &tcov_data.label;
|
||||||
|
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 || \
|
||||||
|
defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64 || \
|
||||||
|
defined TCC_TARGET_RISCV64
|
||||||
|
gen_increment_tcov (&sv);
|
||||||
|
#else
|
||||||
|
vpushv(&sv);
|
||||||
|
inc(0, TOK_INC);
|
||||||
|
vpop();
|
||||||
|
#endif
|
||||||
|
tcov_data.offset = (unsigned char *)ptr - tcov_section->data;
|
||||||
|
tcov_data.ind = ind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcc_tcov_block_end(int line)
|
||||||
|
{
|
||||||
|
if (tcc_state->test_coverage == 0)
|
||||||
|
return;
|
||||||
|
if (tcov_data.offset) {
|
||||||
|
void *ptr = tcov_section->data + tcov_data.offset;
|
||||||
|
unsigned long long nline = line ? line : file->line_num;
|
||||||
|
|
||||||
|
write64le (ptr, (read64le (ptr) & 0xfffffffffull) | (nline << 36));
|
||||||
|
tcov_data.last_offset = tcov_data.offset;
|
||||||
|
tcov_data.offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcc_tcov_check_line(int start)
|
||||||
|
{
|
||||||
|
if (tcc_state->test_coverage == 0)
|
||||||
|
return;
|
||||||
|
if (tcov_data.line != file->line_num) {
|
||||||
|
if ((tcov_data.line + 1) != file->line_num) {
|
||||||
|
tcc_tcov_block_end (tcov_data.line);
|
||||||
|
if (start)
|
||||||
|
tcc_tcov_block_begin ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tcov_data.line = file->line_num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcc_tcov_start(void)
|
||||||
|
{
|
||||||
|
if (tcc_state->test_coverage == 0)
|
||||||
|
return;
|
||||||
|
memset (&tcov_data, 0, sizeof (tcov_data));
|
||||||
|
if (tcov_section == NULL) {
|
||||||
|
tcov_section = new_section(tcc_state, ".tcov", SHT_PROGBITS,
|
||||||
|
SHF_ALLOC | SHF_WRITE);
|
||||||
|
section_ptr_add(tcov_section, 4); // pointer to executable name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcc_tcov_end(void)
|
||||||
|
{
|
||||||
|
if (tcc_state->test_coverage == 0)
|
||||||
|
return;
|
||||||
|
if (tcov_data.last_func_name)
|
||||||
|
section_ptr_add(tcov_section, 1);
|
||||||
|
if (tcov_data.last_file_name)
|
||||||
|
section_ptr_add(tcov_section, 1);
|
||||||
|
}
|
||||||
|
|
||||||
static BufferedFile* put_new_file(TCCState *s1)
|
static BufferedFile* put_new_file(TCCState *s1)
|
||||||
{
|
{
|
||||||
BufferedFile *f = file;
|
BufferedFile *f = file;
|
||||||
|
@ -825,6 +969,7 @@ ST_FUNC int tccgen_compile(TCCState *s1)
|
||||||
local_scope = 0;
|
local_scope = 0;
|
||||||
|
|
||||||
tcc_debug_start(s1);
|
tcc_debug_start(s1);
|
||||||
|
tcc_tcov_start ();
|
||||||
#ifdef TCC_TARGET_ARM
|
#ifdef TCC_TARGET_ARM
|
||||||
arm_init(s1);
|
arm_init(s1);
|
||||||
#endif
|
#endif
|
||||||
|
@ -838,6 +983,7 @@ ST_FUNC int tccgen_compile(TCCState *s1)
|
||||||
check_vstack();
|
check_vstack();
|
||||||
/* end of translation unit info */
|
/* end of translation unit info */
|
||||||
tcc_debug_end(s1);
|
tcc_debug_end(s1);
|
||||||
|
tcc_tcov_end ();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5548,6 +5694,7 @@ ST_FUNC void unary(void)
|
||||||
/* generate line number info */
|
/* generate line number info */
|
||||||
if (tcc_state->do_debug)
|
if (tcc_state->do_debug)
|
||||||
tcc_debug_line(tcc_state);
|
tcc_debug_line(tcc_state);
|
||||||
|
tcc_tcov_check_line (1);
|
||||||
|
|
||||||
sizeof_caller = in_sizeof;
|
sizeof_caller = in_sizeof;
|
||||||
in_sizeof = 0;
|
in_sizeof = 0;
|
||||||
|
@ -6267,8 +6414,10 @@ special_math_val:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s->f.func_noreturn)
|
if (s->f.func_noreturn) {
|
||||||
|
tcc_tcov_block_end (tcov_data.line);
|
||||||
CODE_OFF();
|
CODE_OFF();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -7029,6 +7178,8 @@ again:
|
||||||
goto expr;
|
goto expr;
|
||||||
next();
|
next();
|
||||||
|
|
||||||
|
tcc_tcov_check_line (0);
|
||||||
|
tcc_tcov_block_begin ();
|
||||||
if (t == TOK_IF) {
|
if (t == TOK_IF) {
|
||||||
skip('(');
|
skip('(');
|
||||||
gexpr();
|
gexpr();
|
||||||
|
@ -7109,6 +7260,7 @@ again:
|
||||||
/* jump unless last stmt in top-level block */
|
/* jump unless last stmt in top-level block */
|
||||||
if (tok != '}' || local_scope != 1)
|
if (tok != '}' || local_scope != 1)
|
||||||
rsym = gjmp(rsym);
|
rsym = gjmp(rsym);
|
||||||
|
tcc_tcov_block_end (tcov_data.line);
|
||||||
CODE_OFF();
|
CODE_OFF();
|
||||||
|
|
||||||
} else if (t == TOK_BREAK) {
|
} else if (t == TOK_BREAK) {
|
||||||
|
@ -7342,6 +7494,8 @@ again:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tcc_tcov_check_line (0);
|
||||||
|
tcc_tcov_block_end (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This skips over a stream of tokens containing balanced {} and ()
|
/* This skips over a stream of tokens containing balanced {} and ()
|
||||||
|
@ -7819,6 +7973,7 @@ static void decl_initializer(init_params *p, CType *type, unsigned long c, int f
|
||||||
/* generate line number info */
|
/* generate line number info */
|
||||||
if (!p->sec && tcc_state->do_debug)
|
if (!p->sec && tcc_state->do_debug)
|
||||||
tcc_debug_line(tcc_state);
|
tcc_debug_line(tcc_state);
|
||||||
|
tcc_tcov_check_line (1);
|
||||||
|
|
||||||
if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
|
if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
|
||||||
/* In case of strings we have special handling for arrays, so
|
/* In case of strings we have special handling for arrays, so
|
||||||
|
|
4
tccpe.c
4
tccpe.c
|
@ -1915,6 +1915,10 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
|
||||||
tcc_add_btstub(s1);
|
tcc_add_btstub(s1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (s1->test_coverage) {
|
||||||
|
tcc_add_support(s1, "tcov.o");
|
||||||
|
tcc_add_tcov(s1);
|
||||||
|
}
|
||||||
|
|
||||||
/* grab the startup code from libtcc1.a */
|
/* grab the startup code from libtcc1.a */
|
||||||
#ifdef TCC_IS_NATIVE
|
#ifdef TCC_IS_NATIVE
|
||||||
|
|
1
tccrun.c
1
tccrun.c
|
@ -737,6 +737,7 @@ static void set_exception_handler(void)
|
||||||
struct sigaction sigact;
|
struct sigaction sigact;
|
||||||
/* install TCC signal handlers to print debug info on fatal
|
/* install TCC signal handlers to print debug info on fatal
|
||||||
runtime errors */
|
runtime errors */
|
||||||
|
sigemptyset (&sigact.sa_mask);
|
||||||
sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
|
sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
|
||||||
#if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
|
#if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes
|
||||||
sigact.sa_flags |= SA_ONSTACK;
|
sigact.sa_flags |= SA_ONSTACK;
|
||||||
|
|
|
@ -2200,6 +2200,15 @@ ST_FUNC void gen_cvt_csti(int t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* increment tcov counter */
|
||||||
|
ST_FUNC void gen_increment_tcov (SValue *sv)
|
||||||
|
{
|
||||||
|
o(0x058348); /* addq $1, xxx(%rip) */
|
||||||
|
greloca(cur_text_section, sv->sym, ind, R_X86_64_PC32, -5);
|
||||||
|
gen_le32(0);
|
||||||
|
o(1);
|
||||||
|
}
|
||||||
|
|
||||||
/* computed goto support */
|
/* computed goto support */
|
||||||
void ggoto(void)
|
void ggoto(void)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue