Simplify test-case options.
[platform/upstream/gcc.git] / gcc / gcov-io.c
1 /* File format for coverage information
2    Copyright (C) 1996-2020 Free Software Foundation, Inc.
3    Contributed by Bob Manson <manson@cygnus.com>.
4    Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
5
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
12
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
21
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
25 <http://www.gnu.org/licenses/>.  */
26
27 /* Routines declared in gcov-io.h.  This file should be #included by
28    another source file, after having #included gcov-io.h.  */
29
30 #if !IN_GCOV
31 static void gcov_write_block (unsigned);
32 static gcov_unsigned_t *gcov_write_words (unsigned);
33 #endif
34 static const gcov_unsigned_t *gcov_read_words (unsigned);
35 #if !IN_LIBGCOV
36 static void gcov_allocate (unsigned);
37 #endif
38
39 /* Optimum number of gcov_unsigned_t's read from or written to disk.  */
40 #define GCOV_BLOCK_SIZE (1 << 10)
41
42 struct gcov_var
43 {
44   FILE *file;
45   gcov_position_t start;        /* Position of first byte of block */
46   unsigned offset;              /* Read/write position within the block.  */
47   unsigned length;              /* Read limit in the block.  */
48   unsigned overread;            /* Number of words overread.  */
49   int error;                    /* < 0 overflow, > 0 disk error.  */
50   int mode;                     /* < 0 writing, > 0 reading */
51 #if IN_LIBGCOV
52   /* Holds one block plus 4 bytes, thus all coverage reads & writes
53      fit within this buffer and we always can transfer GCOV_BLOCK_SIZE
54      to and from the disk. libgcov never backtracks and only writes 4
55      or 8 byte objects.  */
56   gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1];
57 #else
58   int endian;                   /* Swap endianness.  */
59   /* Holds a variable length block, as the compiler can write
60      strings and needs to backtrack.  */
61   size_t alloc;
62   gcov_unsigned_t *buffer;
63 #endif
64 } gcov_var;
65
66 /* Save the current position in the gcov file.  */
67 /* We need to expose this function when compiling for gcov-tool.  */
68 #ifndef IN_GCOV_TOOL
69 static inline
70 #endif
71 gcov_position_t
72 gcov_position (void)
73 {
74   gcov_nonruntime_assert (gcov_var.mode > 0); 
75   return gcov_var.start + gcov_var.offset;
76 }
77
78 /* Return nonzero if the error flag is set.  */
79 /* We need to expose this function when compiling for gcov-tool.  */
80 #ifndef IN_GCOV_TOOL
81 static inline
82 #endif
83 int
84 gcov_is_error (void)
85 {
86   return gcov_var.file ? gcov_var.error : 1;
87 }
88
89 #if IN_LIBGCOV
90 /* Move to beginning of file and initialize for writing.  */
91 GCOV_LINKAGE inline void
92 gcov_rewrite (void)
93 {
94   gcov_var.mode = -1; 
95   gcov_var.start = 0;
96   gcov_var.offset = 0;
97   fseek (gcov_var.file, 0L, SEEK_SET);
98 }
99 #endif
100
101 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
102 {
103 #if !IN_LIBGCOV
104   if (gcov_var.endian)
105     {
106       value = (value >> 16) | (value << 16);
107       value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
108     }
109 #endif
110   return value;
111 }
112
113 /* Open a gcov file. NAME is the name of the file to open and MODE
114    indicates whether a new file should be created, or an existing file
115    opened. If MODE is >= 0 an existing file will be opened, if
116    possible, and if MODE is <= 0, a new file will be created. Use
117    MODE=0 to attempt to reopen an existing file and then fall back on
118    creating a new one.  If MODE > 0, the file will be opened in
119    read-only mode.  Otherwise it will be opened for modification.
120    Return zero on failure, non-zero on success.  */
121
122 GCOV_LINKAGE int
123 #if IN_LIBGCOV
124 gcov_open (const char *name)
125 #else
126 gcov_open (const char *name, int mode)
127 #endif
128 {
129 #if IN_LIBGCOV
130   int mode = 0;
131 #endif
132 #if GCOV_LOCKED
133   struct flock s_flock;
134   int fd;
135
136   s_flock.l_whence = SEEK_SET;
137   s_flock.l_start = 0;
138   s_flock.l_len = 0; /* Until EOF.  */
139   s_flock.l_pid = getpid ();
140 #endif
141
142   gcov_nonruntime_assert (!gcov_var.file);
143   gcov_var.start = 0;
144   gcov_var.offset = gcov_var.length = 0;
145   gcov_var.overread = -1u;
146   gcov_var.error = 0;
147 #if !IN_LIBGCOV
148   gcov_var.endian = 0;
149 #endif
150 #if GCOV_LOCKED
151   if (mode > 0)
152     {
153       /* Read-only mode - acquire a read-lock.  */
154       s_flock.l_type = F_RDLCK;
155       /* pass mode (ignored) for compatibility */
156       fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR);
157     }
158   else
159      {
160        /* Write mode - acquire a write-lock.  */
161        s_flock.l_type = F_WRLCK;
162        /* Truncate if force new mode.  */
163        fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666);
164     }
165   if (fd < 0)
166     return 0;
167
168   while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
169     continue;
170
171   gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
172
173   if (!gcov_var.file)
174     {
175       close (fd);
176       return 0;
177     }
178 #else
179   if (mode >= 0)
180     /* Open an existing file.  */
181     gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
182
183   if (gcov_var.file)
184     mode = 1;
185   else if (mode <= 0)
186     /* Create a new file.  */
187     gcov_var.file = fopen (name, "w+b");
188
189   if (!gcov_var.file)
190     return 0;
191 #endif
192
193   gcov_var.mode = mode ? mode : 1;
194
195   setbuf (gcov_var.file, (char *)0);
196
197   return 1;
198 }
199
200 /* Close the current gcov file. Flushes data to disk. Returns nonzero
201    on failure or error flag set.  */
202
203 GCOV_LINKAGE int
204 gcov_close (void)
205 {
206   if (gcov_var.file)
207     {
208 #if !IN_GCOV
209       if (gcov_var.offset && gcov_var.mode < 0)
210         gcov_write_block (gcov_var.offset);
211 #endif
212       fclose (gcov_var.file);
213       gcov_var.file = 0;
214       gcov_var.length = 0;
215     }
216 #if !IN_LIBGCOV
217   free (gcov_var.buffer);
218   gcov_var.alloc = 0;
219   gcov_var.buffer = 0;
220 #endif
221   gcov_var.mode = 0;
222   return gcov_var.error;
223 }
224
225 #if !IN_LIBGCOV
226 /* Check if MAGIC is EXPECTED. Use it to determine endianness of the
227    file. Returns +1 for same endian, -1 for other endian and zero for
228    not EXPECTED.  */
229
230 GCOV_LINKAGE int
231 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
232 {
233   if (magic == expected)
234     return 1;
235   magic = (magic >> 16) | (magic << 16);
236   magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
237   if (magic == expected)
238     {
239       gcov_var.endian = 1;
240       return -1;
241     }
242   return 0;
243 }
244 #endif
245
246 #if !IN_LIBGCOV
247 static void
248 gcov_allocate (unsigned length)
249 {
250   size_t new_size = gcov_var.alloc;
251
252   if (!new_size)
253     new_size = GCOV_BLOCK_SIZE;
254   new_size += length;
255   new_size *= 2;
256
257   gcov_var.alloc = new_size;
258   gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
259 }
260 #endif
261
262 #if !IN_GCOV
263 /* Write out the current block, if needs be.  */
264
265 static void
266 gcov_write_block (unsigned size)
267 {
268   if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
269     gcov_var.error = 1;
270   gcov_var.start += size;
271   gcov_var.offset -= size;
272 }
273
274 /* Allocate space to write BYTES bytes to the gcov file. Return a
275    pointer to those bytes, or NULL on failure.  */
276
277 static gcov_unsigned_t *
278 gcov_write_words (unsigned words)
279 {
280   gcov_unsigned_t *result;
281
282   gcov_nonruntime_assert (gcov_var.mode < 0);
283 #if IN_LIBGCOV
284   if (gcov_var.offset >= GCOV_BLOCK_SIZE)
285     {
286       gcov_write_block (GCOV_BLOCK_SIZE);
287       if (gcov_var.offset)
288         {
289           memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
290         }
291     }
292 #else
293   if (gcov_var.offset + words > gcov_var.alloc)
294     gcov_allocate (gcov_var.offset + words);
295 #endif
296   result = &gcov_var.buffer[gcov_var.offset];
297   gcov_var.offset += words;
298
299   return result;
300 }
301
302 /* Write unsigned VALUE to coverage file.  Sets error flag
303    appropriately.  */
304
305 GCOV_LINKAGE void
306 gcov_write_unsigned (gcov_unsigned_t value)
307 {
308   gcov_unsigned_t *buffer = gcov_write_words (1);
309
310   buffer[0] = value;
311 }
312
313 /* Write counter VALUE to coverage file.  Sets error flag
314    appropriately.  */
315
316 #if IN_LIBGCOV
317 GCOV_LINKAGE void
318 gcov_write_counter (gcov_type value)
319 {
320   gcov_unsigned_t *buffer = gcov_write_words (2);
321
322   buffer[0] = (gcov_unsigned_t) value;
323   if (sizeof (value) > sizeof (gcov_unsigned_t))
324     buffer[1] = (gcov_unsigned_t) (value >> 32);
325   else
326     buffer[1] = 0;
327 }
328 #endif /* IN_LIBGCOV */
329
330 #if !IN_LIBGCOV
331 /* Write STRING to coverage file.  Sets error flag on file
332    error, overflow flag on overflow */
333
334 GCOV_LINKAGE void
335 gcov_write_string (const char *string)
336 {
337   unsigned length = 0;
338   unsigned alloc = 0;
339   gcov_unsigned_t *buffer;
340
341   if (string)
342     {
343       length = strlen (string);
344       alloc = (length + 4) >> 2;
345     }
346
347   buffer = gcov_write_words (1 + alloc);
348
349   buffer[0] = alloc;
350
351   if (alloc > 0)
352     {
353       buffer[alloc] = 0; /* place nul terminators.  */
354       memcpy (&buffer[1], string, length);
355     }
356 }
357 #endif
358
359 #if !IN_LIBGCOV
360 /* Write FILENAME to coverage file.  Sets error flag on file
361    error, overflow flag on overflow */
362
363 GCOV_LINKAGE void
364 gcov_write_filename (const char *filename)
365 {
366   if (profile_abs_path_flag && filename && filename[0]
367       && !(IS_DIR_SEPARATOR (filename[0])
368 #if HAVE_DOS_BASED_FILE_SYSTEM
369            || filename[1] == ':'
370 #endif
371           ))
372     {
373       char *buf = getcwd (NULL, 0);
374       if (buf != NULL && buf[0])
375         {
376           size_t len = strlen (buf);
377           buf = (char*)xrealloc (buf, len + strlen (filename) + 2);
378           if (!IS_DIR_SEPARATOR (buf[len - 1]))
379             strcat (buf, "/");
380           strcat (buf, filename);
381           gcov_write_string (buf);
382           free (buf);
383           return;
384         }
385     }
386
387   gcov_write_string (filename);
388 }
389 #endif
390
391 #if !IN_LIBGCOV
392 /* Write a tag TAG and reserve space for the record length. Return a
393    value to be used for gcov_write_length.  */
394
395 GCOV_LINKAGE gcov_position_t
396 gcov_write_tag (gcov_unsigned_t tag)
397 {
398   gcov_position_t result = gcov_var.start + gcov_var.offset;
399   gcov_unsigned_t *buffer = gcov_write_words (2);
400
401   buffer[0] = tag;
402   buffer[1] = 0;
403
404   return result;
405 }
406
407 /* Write a record length using POSITION, which was returned by
408    gcov_write_tag.  The current file position is the end of the
409    record, and is restored before returning.  Returns nonzero on
410    overflow.  */
411
412 GCOV_LINKAGE void
413 gcov_write_length (gcov_position_t position)
414 {
415   unsigned offset;
416   gcov_unsigned_t length;
417   gcov_unsigned_t *buffer;
418
419   gcov_nonruntime_assert (gcov_var.mode < 0);
420   gcov_nonruntime_assert (position + 2 <= gcov_var.start + gcov_var.offset);
421   gcov_nonruntime_assert (position >= gcov_var.start);
422   offset = position - gcov_var.start;
423   length = gcov_var.offset - offset - 2;
424   buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset];
425   buffer[1] = length;
426   if (gcov_var.offset >= GCOV_BLOCK_SIZE)
427     gcov_write_block (gcov_var.offset);
428 }
429
430 #else /* IN_LIBGCOV */
431
432 /* Write a tag TAG and length LENGTH.  */
433
434 GCOV_LINKAGE void
435 gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
436 {
437   gcov_unsigned_t *buffer = gcov_write_words (2);
438
439   buffer[0] = tag;
440   buffer[1] = length;
441 }
442
443 /* Write a summary structure to the gcov file.  Return nonzero on
444    overflow.  */
445
446 GCOV_LINKAGE void
447 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
448 {
449   gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
450   gcov_write_unsigned (summary->runs);
451   gcov_write_unsigned (summary->sum_max);
452 }
453
454 #endif /* IN_LIBGCOV */
455
456 #endif /*!IN_GCOV */
457
458 /* Return a pointer to read BYTES bytes from the gcov file. Returns
459    NULL on failure (read past EOF).  */
460
461 static const gcov_unsigned_t *
462 gcov_read_words (unsigned words)
463 {
464   const gcov_unsigned_t *result;
465   unsigned excess = gcov_var.length - gcov_var.offset;
466
467   if (gcov_var.mode <= 0)
468     return NULL;
469
470   if (excess < words)
471     {
472       gcov_var.start += gcov_var.offset;
473       if (excess)
474         {
475 #if IN_LIBGCOV
476           memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
477 #else
478           memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset,
479                    excess * 4);
480 #endif
481         }
482       gcov_var.offset = 0;
483       gcov_var.length = excess;
484 #if IN_LIBGCOV
485       excess = GCOV_BLOCK_SIZE;
486 #else
487       if (gcov_var.length + words > gcov_var.alloc)
488         gcov_allocate (gcov_var.length + words);
489       excess = gcov_var.alloc - gcov_var.length;
490 #endif
491       excess = fread (gcov_var.buffer + gcov_var.length,
492                       1, excess << 2, gcov_var.file) >> 2;
493       gcov_var.length += excess;
494       if (gcov_var.length < words)
495         {
496           gcov_var.overread += words - gcov_var.length;
497           gcov_var.length = 0;
498           return 0;
499         }
500     }
501   result = &gcov_var.buffer[gcov_var.offset];
502   gcov_var.offset += words;
503   return result;
504 }
505
506 /* Read unsigned value from a coverage file. Sets error flag on file
507    error, overflow flag on overflow */
508
509 GCOV_LINKAGE gcov_unsigned_t
510 gcov_read_unsigned (void)
511 {
512   gcov_unsigned_t value;
513   const gcov_unsigned_t *buffer = gcov_read_words (1);
514
515   if (!buffer)
516     return 0;
517   value = from_file (buffer[0]);
518   return value;
519 }
520
521 /* Read counter value from a coverage file. Sets error flag on file
522    error, overflow flag on overflow */
523
524 GCOV_LINKAGE gcov_type
525 gcov_read_counter (void)
526 {
527   gcov_type value;
528   const gcov_unsigned_t *buffer = gcov_read_words (2);
529
530   if (!buffer)
531     return 0;
532   value = from_file (buffer[0]);
533   if (sizeof (value) > sizeof (gcov_unsigned_t))
534     value |= ((gcov_type) from_file (buffer[1])) << 32;
535   else if (buffer[1])
536     gcov_var.error = -1;
537
538   return value;
539 }
540
541 /* Mangle filename path of BASE and output new allocated pointer with
542    mangled path.  */
543
544 char *
545 mangle_path (char const *base)
546 {
547   /* Convert '/' to '#', convert '..' to '^',
548      convert ':' to '~' on DOS based file system.  */
549   const char *probe;
550   char *buffer = (char *)xmalloc (strlen (base) + 1);
551   char *ptr = buffer;
552
553 #if HAVE_DOS_BASED_FILE_SYSTEM
554   if (base[0] && base[1] == ':')
555     {
556       ptr[0] = base[0];
557       ptr[1] = '~';
558       ptr += 2;
559       base += 2;
560     }
561 #endif
562   for (; *base; base = probe)
563     {
564       size_t len;
565
566       for (probe = base; *probe; probe++)
567         if (*probe == '/')
568           break;
569       len = probe - base;
570       if (len == 2 && base[0] == '.' && base[1] == '.')
571         *ptr++ = '^';
572       else
573         {
574           memcpy (ptr, base, len);
575           ptr += len;
576         }
577       if (*probe)
578         {
579           *ptr++ = '#';
580           probe++;
581         }
582     }
583
584   /* Terminate the string.  */
585   *ptr = '\0';
586
587   return buffer;
588 }
589
590 /* We need to expose the below function when compiling for gcov-tool.  */
591
592 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
593 /* Read string from coverage file. Returns a pointer to a static
594    buffer, or NULL on empty string. You must copy the string before
595    calling another gcov function.  */
596
597 GCOV_LINKAGE const char *
598 gcov_read_string (void)
599 {
600   unsigned length = gcov_read_unsigned ();
601
602   if (!length)
603     return 0;
604
605   return (const char *) gcov_read_words (length);
606 }
607 #endif
608
609 GCOV_LINKAGE void
610 gcov_read_summary (struct gcov_summary *summary)
611 {
612   summary->runs = gcov_read_unsigned ();
613   summary->sum_max = gcov_read_unsigned ();
614 }
615
616 /* We need to expose the below function when compiling for gcov-tool.  */
617
618 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
619 /* Reset to a known position.  BASE should have been obtained from
620    gcov_position, LENGTH should be a record length.  */
621
622 GCOV_LINKAGE void
623 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
624 {
625   gcov_nonruntime_assert (gcov_var.mode > 0);
626   base += length;
627   if (base - gcov_var.start <= gcov_var.length)
628     gcov_var.offset = base - gcov_var.start;
629   else
630     {
631       gcov_var.offset = gcov_var.length = 0;
632       fseek (gcov_var.file, base << 2, SEEK_SET);
633       gcov_var.start = ftell (gcov_var.file) >> 2;
634     }
635 }
636 #endif
637
638 #if IN_LIBGCOV
639 /* Move to a given position in a gcov file.  */
640
641 GCOV_LINKAGE void
642 gcov_seek (gcov_position_t base)
643 {
644   if (gcov_var.offset)
645     gcov_write_block (gcov_var.offset);
646   fseek (gcov_var.file, base << 2, SEEK_SET);
647   gcov_var.start = ftell (gcov_var.file) >> 2;
648 }
649 #endif
650
651 #if IN_GCOV > 0
652 /* Return the modification time of the current gcov file.  */
653
654 GCOV_LINKAGE time_t
655 gcov_time (void)
656 {
657   struct stat status;
658
659   if (fstat (fileno (gcov_var.file), &status))
660     return 0;
661   else
662     return status.st_mtime;
663 }
664 #endif /* IN_GCOV */