For obj-c stage-final re-use the checksum from the previous stage
[official-gcc.git] / gcc / gcov-io.c
blob4b1e11d45305590bd1b36afb2651a97e00de47fc
1 /* File format for coverage information
2 Copyright (C) 1996-2021 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
11 version.
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.
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 struct gcov_var
34 FILE *file;
35 int error; /* < 0 overflow, > 0 disk error. */
36 int mode; /* < 0 writing, > 0 reading. */
37 int endian; /* Swap endianness. */
38 } gcov_var;
40 /* Save the current position in the gcov file. */
41 /* We need to expose this function when compiling for gcov-tool. */
42 #ifndef IN_GCOV_TOOL
43 static inline
44 #endif
45 gcov_position_t
46 gcov_position (void)
48 return ftell (gcov_var.file);
51 /* Return nonzero if the error flag is set. */
52 /* We need to expose this function when compiling for gcov-tool. */
53 #ifndef IN_GCOV_TOOL
54 static inline
55 #endif
56 int
57 gcov_is_error (void)
59 return gcov_var.file ? gcov_var.error : 1;
62 #if IN_LIBGCOV
63 /* Move to beginning of file and initialize for writing. */
64 GCOV_LINKAGE inline void
65 gcov_rewrite (void)
67 gcov_var.mode = -1;
68 fseek (gcov_var.file, 0L, SEEK_SET);
70 #endif
72 static inline gcov_unsigned_t
73 from_file (gcov_unsigned_t value)
75 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
76 if (gcov_var.endian)
77 return __builtin_bswap32 (value);
78 #endif
79 return value;
82 /* Open a gcov file. NAME is the name of the file to open and MODE
83 indicates whether a new file should be created, or an existing file
84 opened. If MODE is >= 0 an existing file will be opened, if
85 possible, and if MODE is <= 0, a new file will be created. Use
86 MODE=0 to attempt to reopen an existing file and then fall back on
87 creating a new one. If MODE > 0, the file will be opened in
88 read-only mode. Otherwise it will be opened for modification.
89 Return zero on failure, non-zero on success. */
91 GCOV_LINKAGE int
92 #if IN_LIBGCOV
93 gcov_open (const char *name)
94 #else
95 gcov_open (const char *name, int mode)
96 #endif
98 #if IN_LIBGCOV
99 int mode = 0;
100 #endif
101 #if GCOV_LOCKED
102 struct flock s_flock;
103 int fd;
105 s_flock.l_whence = SEEK_SET;
106 s_flock.l_start = 0;
107 s_flock.l_len = 0; /* Until EOF. */
108 s_flock.l_pid = getpid ();
109 #elif GCOV_LOCKED_WITH_LOCKING
110 int fd;
111 #endif
113 gcov_nonruntime_assert (!gcov_var.file);
114 gcov_var.error = 0;
115 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
116 gcov_var.endian = 0;
117 #endif
118 #if GCOV_LOCKED
119 if (mode > 0)
121 /* Read-only mode - acquire a read-lock. */
122 s_flock.l_type = F_RDLCK;
123 /* pass mode (ignored) for compatibility */
124 fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR);
126 else
128 /* Write mode - acquire a write-lock. */
129 s_flock.l_type = F_WRLCK;
130 /* Truncate if force new mode. */
131 fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666);
133 if (fd < 0)
134 return 0;
136 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
137 continue;
139 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
141 if (!gcov_var.file)
143 close (fd);
144 return 0;
146 #elif GCOV_LOCKED_WITH_LOCKING
147 if (mode > 0)
149 /* pass mode (ignored) for compatibility */
150 fd = open (name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
152 else
154 /* Truncate if force new mode. */
155 fd = open (name, O_RDWR | O_BINARY | O_CREAT | (mode < 0 ? O_TRUNC : 0),
156 0666);
158 if (fd < 0)
159 return 0;
161 if (_locking (fd, _LK_LOCK, LONG_MAX) < 0)
163 close (fd);
164 return 0;
167 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
169 if (!gcov_var.file)
171 close (fd);
172 return 0;
174 #else
175 if (mode >= 0)
176 /* Open an existing file. */
177 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
179 if (gcov_var.file)
180 mode = 1;
181 else if (mode <= 0)
182 /* Create a new file. */
183 gcov_var.file = fopen (name, "w+b");
185 if (!gcov_var.file)
186 return 0;
187 #endif
189 gcov_var.mode = mode ? mode : 1;
191 return 1;
194 /* Close the current gcov file. Flushes data to disk. Returns nonzero
195 on failure or error flag set. */
197 GCOV_LINKAGE int
198 gcov_close (void)
200 if (gcov_var.file)
202 fclose (gcov_var.file);
203 gcov_var.file = 0;
205 gcov_var.mode = 0;
206 return gcov_var.error;
209 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
210 /* Check if MAGIC is EXPECTED. Use it to determine endianness of the
211 file. Returns +1 for same endian, -1 for other endian and zero for
212 not EXPECTED. */
214 GCOV_LINKAGE int
215 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
217 if (magic == expected)
218 return 1;
220 if (__builtin_bswap32 (magic) == expected)
222 gcov_var.endian = 1;
223 return -1;
225 return 0;
227 #endif
229 #if !IN_GCOV
230 /* Write unsigned VALUE to coverage file. */
232 GCOV_LINKAGE void
233 gcov_write_unsigned (gcov_unsigned_t value)
235 gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
236 if (r != 1)
237 gcov_var.error = 1;
240 /* Write counter VALUE to coverage file. Sets error flag
241 appropriately. */
243 #if IN_LIBGCOV
244 GCOV_LINKAGE void
245 gcov_write_counter (gcov_type value)
247 gcov_write_unsigned ((gcov_unsigned_t) value);
248 if (sizeof (value) > sizeof (gcov_unsigned_t))
249 gcov_write_unsigned ((gcov_unsigned_t) (value >> 32));
250 else
251 gcov_write_unsigned (0);
253 #endif /* IN_LIBGCOV */
255 #if !IN_LIBGCOV
256 /* Write STRING to coverage file. Sets error flag on file
257 error, overflow flag on overflow */
259 GCOV_LINKAGE void
260 gcov_write_string (const char *string)
262 unsigned length = 0;
264 if (string)
265 length = strlen (string) + 1;
267 gcov_write_unsigned (length);
268 if (length > 0)
270 gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
271 if (r != 1)
272 gcov_var.error = 1;
275 #endif
277 #if !IN_LIBGCOV
278 /* Write FILENAME to coverage file. Sets error flag on file
279 error, overflow flag on overflow */
281 GCOV_LINKAGE void
282 gcov_write_filename (const char *filename)
284 if (profile_abs_path_flag && filename && filename[0]
285 && !(IS_DIR_SEPARATOR (filename[0])
286 #if HAVE_DOS_BASED_FILE_SYSTEM
287 || filename[1] == ':'
288 #endif
291 char *buf = getcwd (NULL, 0);
292 if (buf != NULL && buf[0])
294 size_t len = strlen (buf);
295 buf = (char*)xrealloc (buf, len + strlen (filename) + 2);
296 if (!IS_DIR_SEPARATOR (buf[len - 1]))
297 strcat (buf, "/");
298 strcat (buf, filename);
299 gcov_write_string (buf);
300 free (buf);
301 return;
305 gcov_write_string (filename);
307 #endif
309 /* Move to a given position in a gcov file. */
311 GCOV_LINKAGE void
312 gcov_seek (gcov_position_t base)
314 fseek (gcov_var.file, base, SEEK_SET);
317 #if !IN_LIBGCOV
318 /* Write a tag TAG and reserve space for the record length. Return a
319 value to be used for gcov_write_length. */
321 GCOV_LINKAGE gcov_position_t
322 gcov_write_tag (gcov_unsigned_t tag)
324 gcov_position_t result = gcov_position ();
325 gcov_write_unsigned (tag);
326 gcov_write_unsigned (0);
328 return result;
331 /* Write a record length using POSITION, which was returned by
332 gcov_write_tag. The current file position is the end of the
333 record, and is restored before returning. Returns nonzero on
334 overflow. */
336 GCOV_LINKAGE void
337 gcov_write_length (gcov_position_t position)
339 gcov_position_t current_position = gcov_position ();
340 gcov_nonruntime_assert (gcov_var.mode < 0);
341 gcov_nonruntime_assert (current_position >= position + 2 * GCOV_WORD_SIZE);
343 gcov_seek (position + GCOV_WORD_SIZE);
344 gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE);
345 gcov_seek (current_position);
348 #else /* IN_LIBGCOV */
350 /* Write a tag TAG and length LENGTH. */
352 GCOV_LINKAGE void
353 gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
355 gcov_write_unsigned (tag);
356 gcov_write_unsigned (length);
359 /* Write a summary structure to the gcov file. Return nonzero on
360 overflow. */
362 GCOV_LINKAGE void
363 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
365 gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
366 gcov_write_unsigned (summary->runs);
367 gcov_write_unsigned (summary->sum_max);
370 #endif /* IN_LIBGCOV */
372 #endif /*!IN_GCOV */
374 /* Return a pointer to read COUNT bytes from the gcov file. Returns
375 NULL on failure (read past EOF). */
377 static void *
378 gcov_read_bytes (void *buffer, unsigned count)
380 if (gcov_var.mode <= 0)
381 return NULL;
383 unsigned read = fread (buffer, count, 1, gcov_var.file);
384 if (read != 1)
385 return NULL;
387 return buffer;
390 /* Read WORDS gcov_unsigned_t values from gcov file. */
392 static gcov_unsigned_t *
393 gcov_read_words (void *buffer, unsigned words)
395 return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
398 /* Read unsigned value from a coverage file. Sets error flag on file
399 error, overflow flag on overflow */
401 GCOV_LINKAGE gcov_unsigned_t
402 gcov_read_unsigned (void)
404 gcov_unsigned_t value;
405 gcov_unsigned_t allocated_buffer[1];
406 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
408 if (!buffer)
409 return 0;
411 value = from_file (buffer[0]);
412 return value;
415 /* Read counter value from a coverage file. Sets error flag on file
416 error, overflow flag on overflow */
418 GCOV_LINKAGE gcov_type
419 gcov_read_counter (void)
421 gcov_type value;
422 gcov_unsigned_t allocated_buffer[2];
423 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
425 if (!buffer)
426 return 0;
427 value = from_file (buffer[0]);
428 if (sizeof (value) > sizeof (gcov_unsigned_t))
429 value |= ((gcov_type) from_file (buffer[1])) << 32;
430 else if (buffer[1])
431 gcov_var.error = -1;
433 return value;
436 /* Mangle filename path of BASE and output new allocated pointer with
437 mangled path. */
439 char *
440 mangle_path (char const *base)
442 /* Convert '/' to '#', convert '..' to '^',
443 convert ':' to '~' on DOS based file system. */
444 const char *probe;
445 char *buffer = (char *)xmalloc (strlen (base) + 1);
446 char *ptr = buffer;
448 #if HAVE_DOS_BASED_FILE_SYSTEM
449 if (base[0] && base[1] == ':')
451 ptr[0] = base[0];
452 ptr[1] = '~';
453 ptr += 2;
454 base += 2;
456 #endif
457 for (; *base; base = probe)
459 size_t len;
461 for (probe = base; *probe; probe++)
462 if (*probe == '/')
463 break;
464 len = probe - base;
465 if (len == 2 && base[0] == '.' && base[1] == '.')
466 *ptr++ = '^';
467 else
469 memcpy (ptr, base, len);
470 ptr += len;
472 if (*probe)
474 *ptr++ = '#';
475 probe++;
479 /* Terminate the string. */
480 *ptr = '\0';
482 return buffer;
485 /* We need to expose the below function when compiling for gcov-tool. */
487 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
488 /* Read string from coverage file. Returns a pointer to a static
489 buffer, or NULL on empty string. You must copy the string before
490 calling another gcov function. */
492 GCOV_LINKAGE const char *
493 gcov_read_string (void)
495 unsigned length = gcov_read_unsigned ();
497 if (!length)
498 return 0;
500 void *buffer = XNEWVEC (char *, length);
501 return (const char *) gcov_read_bytes (buffer, length);
503 #endif
505 GCOV_LINKAGE void
506 gcov_read_summary (struct gcov_summary *summary)
508 summary->runs = gcov_read_unsigned ();
509 summary->sum_max = gcov_read_unsigned ();
512 /* We need to expose the below function when compiling for gcov-tool. */
514 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
515 /* Reset to a known position. BASE should have been obtained from
516 gcov_position, LENGTH should be a record length. */
518 GCOV_LINKAGE void
519 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
521 gcov_nonruntime_assert (gcov_var.mode > 0);
522 base += length;
523 fseek (gcov_var.file, base, SEEK_SET);
525 #endif
527 #if IN_GCOV > 0
528 /* Return the modification time of the current gcov file. */
530 GCOV_LINKAGE time_t
531 gcov_time (void)
533 struct stat status;
535 if (fstat (fileno (gcov_var.file), &status))
536 return 0;
537 else
538 return status.st_mtime;
540 #endif /* IN_GCOV */