c++: Implement C++26 P2573R2 - = delete("should have a reason"); [PR114458]
[official-gcc.git] / gcc / gcov-io.cc
blob689ba9cf4477782678ea2e724e80fa125fd48b45
1 /* File format for coverage information
2 Copyright (C) 1996-2024 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 /* Indicates the last gcov file access error or that no error occurred
33 so far. */
34 enum gcov_file_error
36 GCOV_FILE_COUNTER_OVERFLOW = -1,
37 GCOV_FILE_NO_ERROR = 0,
38 GCOV_FILE_WRITE_ERROR = 1,
39 GCOV_FILE_EOF = 2
42 struct gcov_var
44 FILE *file;
45 enum gcov_file_error error;
46 int mode; /* < 0 writing, > 0 reading. */
47 int endian; /* Swap endianness. */
48 #ifdef IN_GCOV_TOOL
49 gcov_position_t pos; /* File position for stdin support. */
50 #endif
51 } gcov_var;
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. */
57 #ifndef IN_GCOV_TOOL
58 static inline
59 #endif
60 gcov_position_t
61 gcov_position (void)
63 #ifdef IN_GCOV_TOOL
64 if (gcov_var.mode == GCOV_MODE_STDIN)
65 return gcov_var.pos;
66 #endif
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. */
72 #ifndef IN_GCOV_TOOL
73 static inline
74 #endif
75 int
76 gcov_is_error (void)
78 return gcov_var.file ? gcov_var.error : 1;
81 #if IN_LIBGCOV
82 /* Move to beginning of file, initialize for writing, and clear file error
83 status. */
85 GCOV_LINKAGE inline void
86 gcov_rewrite (void)
88 gcov_var.mode = -1;
89 gcov_var.error = GCOV_FILE_NO_ERROR;
90 fseek (gcov_var.file, 0L, SEEK_SET);
92 #endif
94 static inline gcov_unsigned_t
95 from_file (gcov_unsigned_t value)
97 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
98 if (gcov_var.endian)
99 return __builtin_bswap32 (value);
100 #endif
101 return 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. */
113 GCOV_LINKAGE int
114 gcov_open (const char *name, int mode)
116 #if GCOV_LOCKED
117 struct flock s_flock;
118 int fd;
120 s_flock.l_whence = SEEK_SET;
121 s_flock.l_start = 0;
122 s_flock.l_len = 0; /* Until EOF. */
123 s_flock.l_pid = getpid ();
124 #elif GCOV_LOCKED_WITH_LOCKING
125 int fd;
126 #endif
128 gcov_nonruntime_assert (!gcov_var.file);
129 gcov_var.error = GCOV_FILE_NO_ERROR;
130 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
131 gcov_var.endian = 0;
132 #endif
133 #ifdef IN_GCOV_TOOL
134 gcov_var.pos = 0;
135 if (!name)
137 gcov_nonruntime_assert (gcov_var.mode > 0);
138 gcov_var.file = stdin;
139 gcov_var.mode = GCOV_MODE_STDIN;
140 return 1;
142 #endif
143 #if GCOV_LOCKED
144 if (mode > 0)
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);
151 else
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);
158 if (fd < 0)
159 return 0;
161 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
162 continue;
164 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
166 if (!gcov_var.file)
168 close (fd);
169 return 0;
171 #elif GCOV_LOCKED_WITH_LOCKING
172 if (mode > 0)
174 /* pass mode (ignored) for compatibility */
175 fd = open (name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
177 else
179 /* Truncate if force new mode. */
180 fd = open (name, O_RDWR | O_BINARY | O_CREAT | (mode < 0 ? O_TRUNC : 0),
181 0666);
183 if (fd < 0)
184 return 0;
186 if (_locking (fd, _LK_LOCK, LONG_MAX) < 0)
188 close (fd);
189 return 0;
192 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
194 if (!gcov_var.file)
196 close (fd);
197 return 0;
199 #else
200 if (mode >= 0)
201 /* Open an existing file. */
202 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
204 if (gcov_var.file)
205 mode = 1;
206 else if (mode <= 0)
207 /* Create a new file. */
208 gcov_var.file = fopen (name, "w+b");
210 if (!gcov_var.file)
211 return 0;
212 #endif
214 gcov_var.mode = mode ? mode : 1;
216 return 1;
219 /* Close the current gcov file. Flushes data to disk. Returns nonzero
220 on failure or error flag set. */
222 GCOV_LINKAGE int
223 gcov_close (void)
225 #ifdef IN_GCOV_TOOL
226 if (gcov_var.file == stdin)
227 gcov_var.file = 0;
228 else
229 #endif
230 if (gcov_var.file)
232 if (fclose (gcov_var.file))
233 gcov_var.error = GCOV_FILE_WRITE_ERROR;
235 gcov_var.file = 0;
237 gcov_var.mode = 0;
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
244 not EXPECTED. */
246 GCOV_LINKAGE int
247 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
249 if (magic == expected)
250 return 1;
252 if (__builtin_bswap32 (magic) == expected)
254 gcov_var.endian = 1;
255 return -1;
257 return 0;
259 #endif
261 #if !IN_GCOV
262 /* Write DATA of LENGTH characters to coverage file. */
264 GCOV_LINKAGE void
265 gcov_write (const void *data, unsigned length)
267 gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
268 if (r != 1)
269 gcov_var.error = GCOV_FILE_WRITE_ERROR;
272 /* Write unsigned VALUE to coverage file. */
274 GCOV_LINKAGE void
275 gcov_write_unsigned (gcov_unsigned_t value)
277 gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
278 if (r != 1)
279 gcov_var.error = GCOV_FILE_WRITE_ERROR;
282 #if !IN_LIBGCOV
283 /* Write STRING to coverage file. Sets error flag on file
284 error, overflow flag on overflow */
286 GCOV_LINKAGE void
287 gcov_write_string (const char *string)
289 unsigned length = 0;
291 if (string)
292 length = strlen (string) + 1;
294 gcov_write_unsigned (length);
295 if (length > 0)
297 gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
298 if (r != 1)
299 gcov_var.error = GCOV_FILE_WRITE_ERROR;
302 #endif
304 #if !IN_LIBGCOV
305 /* Write FILENAME to coverage file. Sets error flag on file
306 error, overflow flag on overflow */
308 GCOV_LINKAGE void
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] == ':'
315 #endif
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]))
324 strcat (buf, "/");
325 strcat (buf, filename);
326 gcov_write_string (buf);
327 free (buf);
328 return;
332 gcov_write_string (filename);
335 /* Move to a given position in a gcov file. */
337 static void
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);
353 return result;
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
359 overflow. */
361 GCOV_LINKAGE void
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. */
377 GCOV_LINKAGE void
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 */
388 #endif /*!IN_GCOV */
390 /* Return a pointer to read COUNT bytes from the gcov file. Returns
391 NULL on failure (read past EOF). */
393 static void *
394 gcov_read_bytes (void *buffer, unsigned count)
396 if (gcov_var.mode <= 0)
397 return NULL;
399 unsigned read = fread (buffer, count, 1, gcov_var.file);
400 if (read != 1)
402 if (feof (gcov_var.file))
403 gcov_var.error = GCOV_FILE_EOF;
404 return NULL;
407 #ifdef IN_GCOV_TOOL
408 gcov_var.pos += count;
409 #endif
410 return buffer;
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);
431 if (!buffer)
432 return 0;
434 value = from_file (buffer[0]);
435 return value;
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)
444 gcov_type value;
445 gcov_unsigned_t allocated_buffer[2];
446 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
448 if (!buffer)
449 return 0;
450 value = from_file (buffer[0]);
451 if (sizeof (value) > sizeof (gcov_unsigned_t))
452 value |= ((gcov_type) from_file (buffer[1])) << 32;
453 else if (buffer[1])
454 gcov_var.error = GCOV_FILE_COUNTER_OVERFLOW;
456 return value;
459 /* Mangle filename path of BASE and output new allocated pointer with
460 mangled path. */
462 char *
463 mangle_path (char const *base)
465 /* Convert '/' to '#', convert '..' to '^',
466 convert ':' to '~' on DOS based file system. */
467 const char *probe;
468 char *buffer = (char *)xmalloc (strlen (base) + 1);
469 char *ptr = buffer;
471 #if HAVE_DOS_BASED_FILE_SYSTEM
472 if (base[0] && base[1] == ':')
474 ptr[0] = base[0];
475 ptr[1] = '~';
476 ptr += 2;
477 base += 2;
479 #endif
480 for (; *base; base = probe)
482 size_t len;
484 for (probe = base; *probe; probe++)
485 if (*probe == '/')
486 break;
487 len = probe - base;
488 if (len == 2 && base[0] == '.' && base[1] == '.')
489 *ptr++ = '^';
490 else
492 memcpy (ptr, base, len);
493 ptr += len;
495 if (*probe)
497 *ptr++ = '#';
498 probe++;
502 /* Terminate the string. */
503 *ptr = '\0';
505 return buffer;
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
513 empty string. */
515 GCOV_LINKAGE const char *
516 gcov_read_string (void)
518 unsigned length = gcov_read_unsigned ();
520 if (!length)
521 return 0;
523 void *buffer = XNEWVEC (char *, length);
524 return (const char *) gcov_read_bytes (buffer, length);
526 #endif
528 GCOV_LINKAGE void
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. */
541 GCOV_LINKAGE void
542 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
544 gcov_nonruntime_assert (gcov_var.mode > 0);
545 base += length;
546 #ifdef IN_GCOV_TOOL
547 if (gcov_var.mode == GCOV_MODE_STDIN)
549 while (gcov_var.pos < base)
551 ++gcov_var.pos;
552 (void)fgetc (gcov_var.file);
554 return;
556 #endif
557 fseek (gcov_var.file, base, SEEK_SET);
559 #endif
561 #if IN_GCOV > 0
562 /* Return the modification time of the current gcov file. */
564 GCOV_LINKAGE time_t
565 gcov_time (void)
567 struct stat status;
569 if (fstat (fileno (gcov_var.file), &status))
570 return 0;
571 else
572 return status.st_mtime;
574 #endif /* IN_GCOV */