aboutsummaryrefslogtreecommitdiff
path: root/tinycc/lib/tcov.c
diff options
context:
space:
mode:
authorUneven Prankster <unevenprankster@protonmail.com>2023-07-12 13:22:29 -0300
committerUneven Prankster <unevenprankster@protonmail.com>2023-07-12 13:22:29 -0300
commitfa2bdd711212ba6b7a94a20971e8bfa281e73296 (patch)
tree6713b3c0379507d49558287b71dd360ce188a2f0 /tinycc/lib/tcov.c
lol
Diffstat (limited to 'tinycc/lib/tcov.c')
-rw-r--r--tinycc/lib/tcov.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/tinycc/lib/tcov.c b/tinycc/lib/tcov.c
new file mode 100644
index 0000000..32b7d18
--- /dev/null
+++ b/tinycc/lib/tcov.c
@@ -0,0 +1,428 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#ifndef _WIN32
+#include <unistd.h>
+#include <errno.h>
+#else
+#include <windows.h>
+#include <io.h>
+#endif
+
+/* 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 FILE *open_tcov_file (char *cov_filename)
+{
+ int fd;
+#ifndef _WIN32
+ struct flock lock;
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0; /* Until EOF. */
+ lock.l_pid = getpid ();
+#endif
+ fd = open (cov_filename, O_RDWR | O_CREAT, 0666);
+ if (fd < 0)
+ return NULL;
+
+#ifndef _WIN32
+ while (fcntl (fd, F_SETLKW, &lock) && errno == EINTR)
+ continue;
+#else
+ {
+ OVERLAPPED overlapped = { 0 };
+ LockFileEx((HANDLE)_get_osfhandle(fd), LOCKFILE_EXCLUSIVE_LOCK,
+ 0, 1, 0, &overlapped);
+ }
+#endif
+
+ return fdopen (fd, "r+");
+}
+
+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;
+ unsigned char *start = p;
+ 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 += -(p - start) & 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, FILE *fp,
+ unsigned int *pruns)
+{
+ unsigned int runs;
+ 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;
+ }
+}
+
+/* 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;
+
+ fp = open_tcov_file (cov_filename);
+ if (fp == NULL) {
+ fprintf (stderr, "Cannot create coverage file: %s\n", cov_filename);
+ return;
+ }
+ file = sort_test_coverage (p);
+ merge_test_coverage (file, fp, &runs);
+ fseek (fp, 0, SEEK_SET);
+ 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
+ break;
+ }
+ if (same_line)
+ lline++;
+
+ 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);
+}