fixup! riscv: Implement large addend for global address
[tinycc.git] / lib / tcov.c
blobaf09a734f65275f9422a8742411e6cf1e68dd6bb
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <fcntl.h>
5 #ifndef _WIN32
6 #include <unistd.h>
7 #include <errno.h>
8 #else
9 #include <windows.h>
10 #include <io.h>
11 #endif
13 /* section layout (all little endian):
14 32bit offset to executable/so file name
15 filename \0
16 function name \0
17 align to 64 bits
18 64bit function start line
19 64bits end_line(28bits) / start_line(28bits) / flag=0xff(8bits)
20 64bits counter
24 executable/so file name \0
27 typedef struct tcov_line {
28 unsigned int fline;
29 unsigned int lline;
30 unsigned long long count;
31 } tcov_line;
33 typedef struct tcov_function {
34 char *function;
35 unsigned int first_line;
36 unsigned int n_line;
37 unsigned int m_line;
38 tcov_line *line;
39 } tcov_function;
41 typedef struct tcov_file {
42 char *filename;
43 unsigned int n_func;
44 unsigned int m_func;
45 tcov_function *func;
46 struct tcov_file *next;
47 } tcov_file;
49 static FILE *open_tcov_file (char *cov_filename)
51 int fd;
52 #ifndef _WIN32
53 struct flock lock;
55 lock.l_type = F_WRLCK;
56 lock.l_whence = SEEK_SET;
57 lock.l_start = 0;
58 lock.l_len = 0; /* Until EOF. */
59 lock.l_pid = getpid ();
60 #endif
61 fd = open (cov_filename, O_RDWR | O_CREAT, 0666);
62 if (fd < 0)
63 return NULL;
65 #ifndef _WIN32
66 while (fcntl (fd, F_SETLKW, &lock) && errno == EINTR)
67 continue;
68 #else
70 OVERLAPPED overlapped = { 0 };
71 LockFileEx((HANDLE)_get_osfhandle(fd), LOCKFILE_EXCLUSIVE_LOCK,
72 0, 1, 0, &overlapped);
74 #endif
76 return fdopen (fd, "r+");
79 static unsigned long long get_value(unsigned char *p, int size)
81 unsigned long long value = 0;
83 p += size;
84 while (size--)
85 value = (value << 8) | *--p;
86 return value;
89 static int sort_func (const void *p, const void *q)
91 const tcov_function *pp = (const tcov_function *) p;
92 const tcov_function *pq = (const tcov_function *) q;
94 return pp->first_line > pq->first_line ? 1 :
95 pp->first_line < pq->first_line ? -1 : 0;
98 static int sort_line (const void *p, const void *q)
100 const tcov_line *pp = (const tcov_line *) p;
101 const tcov_line *pq = (const tcov_line *) q;
103 return pp->fline > pq->fline ? 1 :
104 pp->fline < pq->fline ? -1 :
105 pp->count < pq->count ? 1 :
106 pp->count > pq->count ? -1 : 0;
109 /* sort to let inline functions work */
110 static tcov_file *sort_test_coverage (unsigned char *p)
112 int i, j, k;
113 unsigned char *start = p;
114 tcov_file *file = NULL;
115 tcov_file *nfile;
117 p += 4;
118 while (*p) {
119 char *filename = (char *)p;
120 size_t len = strlen (filename);
122 nfile = file;
123 while (nfile) {
124 if (strcmp (nfile->filename, filename) == 0)
125 break;
126 nfile = nfile->next;
128 if (nfile == NULL) {
129 nfile = malloc (sizeof(tcov_file));
130 if (nfile == NULL) {
131 fprintf (stderr, "Malloc error test_coverage\n");
132 return file;
134 nfile->filename = filename;
135 nfile->n_func = 0;
136 nfile->m_func = 0;
137 nfile->func = NULL;
138 nfile->next = NULL;
139 if (file == NULL)
140 file = nfile;
141 else {
142 tcov_file *lfile = file;
144 while (lfile->next)
145 lfile = lfile->next;
146 lfile->next = nfile;
149 p += len + 1;
150 while (*p) {
151 int i;
152 char *function = (char *)p;
153 tcov_function *func;
155 p += strlen (function) + 1;
156 p += -(p - start) & 7;
157 for (i = 0; i < nfile->n_func; i++) {
158 func = &nfile->func[i];
159 if (strcmp (func->function, function) == 0)
160 break;
162 if (i == nfile->n_func) {
163 if (nfile->n_func >= nfile->m_func) {
164 nfile->m_func = nfile->m_func == 0 ? 4 : nfile->m_func * 2;
165 nfile->func = realloc (nfile->func,
166 nfile->m_func *
167 sizeof (tcov_function));
168 if (nfile->func == NULL) {
169 fprintf (stderr, "Realloc error test_coverage\n");
170 return file;
173 func = &nfile->func[nfile->n_func++];
174 func->function = function;
175 func->first_line = get_value (p, 8);
176 func->n_line = 0;
177 func->m_line = 0;
178 func->line = NULL;
180 p += 8;
181 while (*p) {
182 tcov_line *line;
183 unsigned long long val;
185 if (func->n_line >= func->m_line) {
186 func->m_line = func->m_line == 0 ? 4 : func->m_line * 2;
187 func->line = realloc (func->line,
188 func->m_line * sizeof (tcov_line));
189 if (func->line == NULL) {
190 fprintf (stderr, "Realloc error test_coverage\n");
191 return file;
194 line = &func->line[func->n_line++];
195 val = get_value (p, 8);
196 line->fline = (val >> 8) & 0xfffffffULL;
197 line->lline = val >> 36;
198 line->count = get_value (p + 8, 8);
199 p += 16;
201 p++;
203 p++;
205 nfile = file;
206 while (nfile) {
207 qsort (nfile->func, nfile->n_func, sizeof (tcov_function), sort_func);
208 for (i = 0; i < nfile->n_func; i++) {
209 tcov_function *func = &nfile->func[i];
210 qsort (func->line, func->n_line, sizeof (tcov_line), sort_line);
212 nfile = nfile->next;
214 return file;
217 /* merge with previous tcov file */
218 static void merge_test_coverage (tcov_file *file, FILE *fp,
219 unsigned int *pruns)
221 unsigned int runs;
222 char *p;
223 char str[10000];
225 *pruns = 1;
226 if (fp == NULL)
227 return;
228 if (fgets(str, sizeof(str), fp) &&
229 (p = strrchr (str, ':')) &&
230 (sscanf (p + 1, "%u", &runs) == 1))
231 *pruns = runs + 1;
232 while (file) {
233 int i;
234 size_t len = strlen (file->filename);
236 while (fgets(str, sizeof(str), fp) &&
237 (p = strstr(str, "0:File:")) == NULL) {}
238 if ((p = strstr(str, "0:File:")) == NULL ||
239 strncmp (p + strlen("0:File:"), file->filename, len) != 0 ||
240 p[strlen("0:File:") + len] != ' ')
241 break;
242 for (i = 0; i < file->n_func; i++) {
243 int j;
244 tcov_function *func = &file->func[i];
245 unsigned int next_zero = 0;
246 unsigned int curline = 0;
248 for (j = 0; j < func->n_line; j++) {
249 tcov_line *line = &func->line[j];
250 unsigned int fline = line->fline;
251 unsigned long long count;
252 unsigned int tmp;
253 char c;
255 while (curline < fline &&
256 fgets(str, sizeof(str), fp))
257 if ((p = strchr(str, ':')) &&
258 sscanf (p + 1, "%u", &tmp) == 1)
259 curline = tmp;
260 if (sscanf (str, "%llu%c\n", &count, &c) == 2) {
261 if (next_zero == 0)
262 line->count += count;
263 next_zero = c == '*';
267 file = file->next;
271 /* store tcov data in file */
272 void __store_test_coverage (unsigned char * p)
274 int i, j;
275 unsigned int files;
276 unsigned int funcs;
277 unsigned int blocks;
278 unsigned int blocks_run;
279 unsigned int runs;
280 char *cov_filename = (char *)p + get_value (p, 4);
281 FILE *fp;
282 char *q;
283 tcov_file *file;
284 tcov_file *nfile;
285 tcov_function *func;
287 fp = open_tcov_file (cov_filename);
288 if (fp == NULL) {
289 fprintf (stderr, "Cannot create coverage file: %s\n", cov_filename);
290 return;
292 file = sort_test_coverage (p);
293 merge_test_coverage (file, fp, &runs);
294 fseek (fp, 0, SEEK_SET);
295 fprintf (fp, " -: 0:Runs:%u\n", runs);
296 files = 0;
297 funcs = 0;
298 blocks = 0;
299 blocks_run = 0;
300 nfile = file;
301 while (nfile) {
302 files++;
303 for (i = 0; i < nfile->n_func; i++) {
304 func = &nfile->func[i];
305 funcs++;
306 for (j = 0; j < func->n_line; j++) {
307 blocks++;
308 blocks_run += func->line[j].count != 0;
311 nfile = nfile->next;
313 if (blocks == 0)
314 blocks = 1;
315 fprintf (fp, " -: 0:All:%s Files:%u Functions:%u %.02f%%\n",
316 cov_filename, files, funcs, 100.0 * (double) blocks_run / blocks);
317 nfile = file;
318 while (nfile) {
319 FILE *src = fopen (nfile->filename, "r");
320 unsigned int curline = 1;
321 char str[10000];
323 if (src == NULL)
324 goto next;
325 funcs = 0;
326 blocks = 0;
327 blocks_run = 0;
328 for (i = 0; i < nfile->n_func; i++) {
329 func = &nfile->func[i];
330 funcs++;
331 for (j = 0; j < func->n_line; j++) {
332 blocks++;
333 blocks_run += func->line[j].count != 0;
336 if (blocks == 0)
337 blocks = 1;
338 fprintf (fp, " -: 0:File:%s Functions:%u %.02f%%\n",
339 nfile->filename, funcs, 100.0 * (double) blocks_run / blocks);
340 for (i = 0; i < nfile->n_func; i++) {
341 func = &nfile->func[i];
343 while (curline < func->first_line &&
344 fgets(str, sizeof(str), src))
345 fprintf (fp, " -:%5u:%s", curline++, str);
346 blocks = 0;
347 blocks_run = 0;
348 for (j = 0; j < func->n_line; j++) {
349 blocks++;
350 blocks_run += func->line[j].count != 0;
352 if (blocks == 0)
353 blocks = 1;
354 fprintf (fp, " -: 0:Function:%s %.02f%%\n",
355 func->function, 100.0 * (double) blocks_run / blocks);
356 #if 0
357 for (j = 0; j < func->n_line; j++) {
358 unsigned int fline = func->line[j].fline;
359 unsigned int lline = func->line[j].lline;
360 unsigned long long count = func->line[j].count;
362 fprintf (fp, "%u %u %llu\n", fline, lline, count);
364 #endif
365 for (j = 0; j < func->n_line;) {
366 unsigned int fline = func->line[j].fline;
367 unsigned int lline = func->line[j].lline;
368 unsigned long long count = func->line[j].count;
369 unsigned int has_zero = 0;
370 unsigned int same_line = fline == lline;
372 j++;
373 while (j < func->n_line) {
374 unsigned int nfline = func->line[j].fline;
375 unsigned int nlline = func->line[j].lline;
376 unsigned long long ncount = func->line[j].count;
378 if (fline == nfline) {
379 if (ncount == 0)
380 has_zero = 1;
381 else if (ncount > count)
382 count = ncount;
383 same_line = nfline == nlline;
384 lline = nlline;
385 j++;
387 else
388 break;
390 if (same_line)
391 lline++;
393 while (curline < fline &&
394 fgets(str, sizeof(str), src))
395 fprintf (fp, " -:%5u:%s", curline++, str);
396 while (curline < lline &&
397 fgets(str, sizeof(str), src)) {
398 if (count == 0)
399 fprintf (fp, " #####:%5u:%s",
400 curline, str);
401 else if (has_zero)
402 fprintf (fp, "%8llu*:%5u:%s",
403 count, curline, str);
404 else
405 fprintf (fp, "%9llu:%5u:%s",
406 count, curline, str);
407 curline++;
411 while (fgets(str, sizeof(str), src))
412 fprintf (fp, " -:%5u:%s", curline++, str);
413 fclose (src);
414 next:
415 nfile = nfile->next;
417 while (file) {
418 for (i = 0; i < file->n_func; i++) {
419 func = &file->func[i];
420 free (func->line);
422 free (file->func);
423 nfile = file;
424 file = file->next;
425 free (nfile);
427 fclose (fp);