1 /* File format for coverage information
2 Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 Contributed by Bob Manson <manson@cygnus.com>.
4 Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
6 This file is part of GCC.
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
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
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.
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/>. */
27 /* Routines declared in gcov-io.h. This file should be #included by
28 another source file, after having #included gcov-io.h. */
30 static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned);
32 /* Indicates the last gcov file access error or that no error occurred
36 GCOV_FILE_COUNTER_OVERFLOW = -1,
37 GCOV_FILE_NO_ERROR = 0,
38 GCOV_FILE_WRITE_ERROR = 1,
45 enum gcov_file_error error;
46 int mode; /* < 0 writing, > 0 reading. */
47 int endian; /* Swap endianness. */
49 gcov_position_t pos; /* File position for stdin support. */
53 #define GCOV_MODE_STDIN 2
55 /* Save the current position in the gcov file. */
56 /* We need to expose this function when compiling for gcov-tool. */
64 if (gcov_var.mode == GCOV_MODE_STDIN)
67 return ftell (gcov_var.file);
70 /* Return nonzero if the error flag is set. */
71 /* We need to expose this function when compiling for gcov-tool. */
78 return gcov_var.file ? gcov_var.error : 1;
82 /* Move to beginning of file, initialize for writing, and clear file error
85 GCOV_LINKAGE inline void
89 gcov_var.error = GCOV_FILE_NO_ERROR;
90 fseek (gcov_var.file, 0L, SEEK_SET);
94 static inline gcov_unsigned_t
95 from_file (gcov_unsigned_t value)
97 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
99 return __builtin_bswap32 (value);
104 /* Open a gcov file. NAME is the name of the file to open and MODE
105 indicates whether a new file should be created, or an existing file
106 opened. If MODE is >= 0 an existing file will be opened, if
107 possible, and if MODE is <= 0, a new file will be created. Use
108 MODE=0 to attempt to reopen an existing file and then fall back on
109 creating a new one. If MODE > 0, the file will be opened in
110 read-only mode. Otherwise it will be opened for modification.
111 Return zero on failure, non-zero on success. */
114 gcov_open (const char *name, int mode)
117 struct flock s_flock;
120 s_flock.l_whence = SEEK_SET;
122 s_flock.l_len = 0; /* Until EOF. */
123 s_flock.l_pid = getpid ();
124 #elif GCOV_LOCKED_WITH_LOCKING
128 gcov_nonruntime_assert (!gcov_var.file);
129 gcov_var.error = GCOV_FILE_NO_ERROR;
130 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
137 gcov_nonruntime_assert (gcov_var.mode > 0);
138 gcov_var.file = stdin;
139 gcov_var.mode = GCOV_MODE_STDIN;
146 /* Read-only mode - acquire a read-lock. */
147 s_flock.l_type = F_RDLCK;
148 /* pass mode (ignored) for compatibility */
149 fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR);
153 /* Write mode - acquire a write-lock. */
154 s_flock.l_type = F_WRLCK;
155 /* Truncate if force new mode. */
156 fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666);
161 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
164 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
171 #elif GCOV_LOCKED_WITH_LOCKING
174 /* pass mode (ignored) for compatibility */
175 fd = open (name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
179 /* Truncate if force new mode. */
180 fd = open (name, O_RDWR | O_BINARY | O_CREAT | (mode < 0 ? O_TRUNC : 0),
186 if (_locking (fd, _LK_LOCK, LONG_MAX) < 0)
192 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
201 /* Open an existing file. */
202 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
207 /* Create a new file. */
208 gcov_var.file = fopen (name, "w+b");
214 gcov_var.mode = mode ? mode : 1;
219 /* Close the current gcov file. Flushes data to disk. Returns nonzero
220 on failure or error flag set. */
226 if (gcov_var.file == stdin)
232 if (fclose (gcov_var.file))
233 gcov_var.error = GCOV_FILE_WRITE_ERROR;
238 return gcov_var.error;
241 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
242 /* Check if MAGIC is EXPECTED. Use it to determine endianness of the
243 file. Returns +1 for same endian, -1 for other endian and zero for
247 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
249 if (magic == expected)
252 if (__builtin_bswap32 (magic) == expected)
262 /* Write DATA of LENGTH characters to coverage file. */
265 gcov_write (const void *data, unsigned length)
267 gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
269 gcov_var.error = GCOV_FILE_WRITE_ERROR;
272 /* Write unsigned VALUE to coverage file. */
275 gcov_write_unsigned (gcov_unsigned_t value)
277 gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
279 gcov_var.error = GCOV_FILE_WRITE_ERROR;
283 /* Write STRING to coverage file. Sets error flag on file
284 error, overflow flag on overflow */
287 gcov_write_string (const char *string)
292 length = strlen (string) + 1;
294 gcov_write_unsigned (length);
297 gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
299 gcov_var.error = GCOV_FILE_WRITE_ERROR;
305 /* Write FILENAME to coverage file. Sets error flag on file
306 error, overflow flag on overflow */
309 gcov_write_filename (const char *filename)
311 if (profile_abs_path_flag && filename && filename[0]
312 && !(IS_DIR_SEPARATOR (filename[0])
313 #if HAVE_DOS_BASED_FILE_SYSTEM
314 || filename[1] == ':'
318 char *buf = getcwd (NULL, 0);
319 if (buf != NULL && buf[0])
321 size_t len = strlen (buf);
322 buf = (char*)xrealloc (buf, len + strlen (filename) + 2);
323 if (!IS_DIR_SEPARATOR (buf[len - 1]))
325 strcat (buf, filename);
326 gcov_write_string (buf);
332 gcov_write_string (filename);
335 /* Move to a given position in a gcov file. */
338 gcov_seek (gcov_position_t base)
340 fseek (gcov_var.file, base, SEEK_SET);
343 /* Write a tag TAG and reserve space for the record length. Return a
344 value to be used for gcov_write_length. */
346 GCOV_LINKAGE gcov_position_t
347 gcov_write_tag (gcov_unsigned_t tag)
349 gcov_position_t result = gcov_position ();
350 gcov_write_unsigned (tag);
351 gcov_write_unsigned (0);
356 /* Write a record length using POSITION, which was returned by
357 gcov_write_tag. The current file position is the end of the
358 record, and is restored before returning. Returns nonzero on
362 gcov_write_length (gcov_position_t position)
364 gcov_position_t current_position = gcov_position ();
365 gcov_nonruntime_assert (gcov_var.mode < 0);
366 gcov_nonruntime_assert (current_position >= position + 2 * GCOV_WORD_SIZE);
368 gcov_seek (position + GCOV_WORD_SIZE);
369 gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE);
370 gcov_seek (current_position);
373 #else /* IN_LIBGCOV */
375 /* Write an object summary structure to the gcov file. */
378 gcov_write_object_summary (const struct gcov_summary *summary)
380 gcov_write_unsigned (GCOV_TAG_OBJECT_SUMMARY);
381 gcov_write_unsigned (GCOV_TAG_OBJECT_SUMMARY_LENGTH);
382 gcov_write_unsigned (summary->runs);
383 gcov_write_unsigned (summary->sum_max);
386 #endif /* IN_LIBGCOV */
390 /* Return a pointer to read COUNT bytes from the gcov file. Returns
391 NULL on failure (read past EOF). */
394 gcov_read_bytes (void *buffer, unsigned count)
396 if (gcov_var.mode <= 0)
399 unsigned read = fread (buffer, count, 1, gcov_var.file);
402 if (feof (gcov_var.file))
403 gcov_var.error = GCOV_FILE_EOF;
408 gcov_var.pos += count;
413 /* Read WORDS gcov_unsigned_t values from gcov file. */
415 static gcov_unsigned_t *
416 gcov_read_words (void *buffer, unsigned words)
418 return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
421 /* Read unsigned value from a coverage file. Sets error flag on file
422 error, overflow flag on overflow */
424 GCOV_LINKAGE gcov_unsigned_t
425 gcov_read_unsigned (void)
427 gcov_unsigned_t value;
428 gcov_unsigned_t allocated_buffer[1];
429 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
434 value = from_file (buffer[0]);
438 /* Read counter value from a coverage file. Sets error flag on file
439 error, overflow flag on overflow */
441 GCOV_LINKAGE gcov_type
442 gcov_read_counter (void)
445 gcov_unsigned_t allocated_buffer[2];
446 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
450 value = from_file (buffer[0]);
451 if (sizeof (value) > sizeof (gcov_unsigned_t))
452 value |= ((gcov_type) from_file (buffer[1])) << 32;
454 gcov_var.error = GCOV_FILE_COUNTER_OVERFLOW;
459 /* Mangle filename path of BASE and output new allocated pointer with
463 mangle_path (char const *base)
465 /* Convert '/' to '#', convert '..' to '^',
466 convert ':' to '~' on DOS based file system. */
468 char *buffer = (char *)xmalloc (strlen (base) + 1);
471 #if HAVE_DOS_BASED_FILE_SYSTEM
472 if (base[0] && base[1] == ':')
480 for (; *base; base = probe)
484 for (probe = base; *probe; probe++)
488 if (len == 2 && base[0] == '.' && base[1] == '.')
492 memcpy (ptr, base, len);
502 /* Terminate the string. */
508 /* We need to expose the below function when compiling for gcov-tool. */
510 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
511 /* Read string from coverage file. Allocate the buffer for the string
512 from the heap or die. Return a pointer to the string, or NULL on
515 GCOV_LINKAGE const char *
516 gcov_read_string (void)
518 unsigned length = gcov_read_unsigned ();
523 void *buffer = XNEWVEC (char *, length);
524 return (const char *) gcov_read_bytes (buffer, length);
529 gcov_read_summary (struct gcov_summary *summary)
531 summary->runs = gcov_read_unsigned ();
532 summary->sum_max = gcov_read_unsigned ();
535 /* We need to expose the below function when compiling for gcov-tool. */
537 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
538 /* Reset to a known position. BASE should have been obtained from
539 gcov_position, LENGTH should be a record length. */
542 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
544 gcov_nonruntime_assert (gcov_var.mode > 0);
547 if (gcov_var.mode == GCOV_MODE_STDIN)
549 while (gcov_var.pos < base)
552 (void)fgetc (gcov_var.file);
557 fseek (gcov_var.file, base, SEEK_SET);
562 /* Return the modification time of the current gcov file. */
569 if (fstat (fileno (gcov_var.file), &status))
572 return status.st_mtime;