]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/gcov-io.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / gcov-io.c
CommitLineData
ca29da43 1/* File format for coverage information
99dee823 2 Copyright (C) 1996-2021 Free Software Foundation, Inc.
ca29da43
NS
3 Contributed by Bob Manson <manson@cygnus.com>.
4 Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
5
6This file is part of GCC.
7
8GCC is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
9dcd6f09 10Software Foundation; either version 3, or (at your option) any later
ca29da43
NS
11version.
12
13GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
ad01c437
JM
18Under Section 7 of GPL version 3, you are granted additional
19permissions described in the GCC Runtime Library Exception, version
203.1, as published by the Free Software Foundation.
21
22You should have received a copy of the GNU General Public License and
23a copy of the GCC Runtime Library Exception along with this program;
24see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
9dcd6f09 25<http://www.gnu.org/licenses/>. */
ca29da43
NS
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
23eb66d1 30static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned);
40d6b753 31
88d67744 32struct gcov_var
40d6b753
RX
33{
34 FILE *file;
40d6b753 35 int error; /* < 0 overflow, > 0 disk error. */
23eb66d1 36 int mode; /* < 0 writing, > 0 reading. */
17d1594b 37 int endian; /* Swap endianness. */
40d6b753
RX
38} gcov_var;
39
40/* Save the current position in the gcov file. */
c77556a5
RX
41/* We need to expose this function when compiling for gcov-tool. */
42#ifndef IN_GCOV_TOOL
43static inline
44#endif
45gcov_position_t
40d6b753
RX
46gcov_position (void)
47{
23eb66d1 48 return ftell (gcov_var.file);
40d6b753
RX
49}
50
51/* Return nonzero if the error flag is set. */
c77556a5
RX
52/* We need to expose this function when compiling for gcov-tool. */
53#ifndef IN_GCOV_TOOL
54static inline
55#endif
56int
40d6b753
RX
57gcov_is_error (void)
58{
59 return gcov_var.file ? gcov_var.error : 1;
60}
61
62#if IN_LIBGCOV
63/* Move to beginning of file and initialize for writing. */
64GCOV_LINKAGE inline void
65gcov_rewrite (void)
66{
40d6b753 67 gcov_var.mode = -1;
40d6b753
RX
68 fseek (gcov_var.file, 0L, SEEK_SET);
69}
70#endif
71
23eb66d1 72static inline gcov_unsigned_t
73from_file (gcov_unsigned_t value)
160e2e4f 74{
17d1594b 75#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
160e2e4f 76 if (gcov_var.endian)
23eb66d1 77 return __builtin_bswap32 (value);
160e2e4f
NS
78#endif
79 return value;
80}
81
ca29da43
NS
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
ad467730
NV
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
fc0911e0 87 creating a new one. If MODE > 0, the file will be opened in
ad467730 88 read-only mode. Otherwise it will be opened for modification.
fc0911e0 89 Return zero on failure, non-zero on success. */
ca29da43
NS
90
91GCOV_LINKAGE int
160e2e4f
NS
92#if IN_LIBGCOV
93gcov_open (const char *name)
94#else
ca29da43 95gcov_open (const char *name, int mode)
160e2e4f 96#endif
ca29da43 97{
160e2e4f 98#if IN_LIBGCOV
74d2b907 99 int mode = 0;
160e2e4f 100#endif
474f141e 101#if GCOV_LOCKED
ca29da43 102 struct flock s_flock;
c2cd64b5 103 int fd;
ca29da43 104
ca29da43
NS
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 ();
9ec469f5
EB
109#elif GCOV_LOCKED_WITH_LOCKING
110 int fd;
ca29da43 111#endif
b8698a0f 112
e3f0315f 113 gcov_nonruntime_assert (!gcov_var.file);
7d63a2fa 114 gcov_var.error = 0;
de8bfcc8 115#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
160e2e4f
NS
116 gcov_var.endian = 0;
117#endif
c2cd64b5
JJ
118#if GCOV_LOCKED
119 if (mode > 0)
ad467730
NV
120 {
121 /* Read-only mode - acquire a read-lock. */
122 s_flock.l_type = F_RDLCK;
2588b26e
RM
123 /* pass mode (ignored) for compatibility */
124 fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR);
ad467730 125 }
fc0911e0 126 else
55428cc3
LA
127 {
128 /* Write mode - acquire a write-lock. */
129 s_flock.l_type = F_WRLCK;
fc0911e0
NS
130 /* Truncate if force new mode. */
131 fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666);
ad467730 132 }
c2cd64b5
JJ
133 if (fd < 0)
134 return 0;
135
136 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
137 continue;
138
ad467730
NV
139 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
140
9ec469f5
EB
141 if (!gcov_var.file)
142 {
143 close (fd);
144 return 0;
145 }
146#elif GCOV_LOCKED_WITH_LOCKING
147 if (mode > 0)
148 {
149 /* pass mode (ignored) for compatibility */
150 fd = open (name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
151 }
152 else
153 {
154 /* Truncate if force new mode. */
155 fd = open (name, O_RDWR | O_BINARY | O_CREAT | (mode < 0 ? O_TRUNC : 0),
156 0666);
157 }
158 if (fd < 0)
159 return 0;
160
161 if (_locking (fd, _LK_LOCK, LONG_MAX) < 0)
162 {
163 close (fd);
164 return 0;
165 }
166
167 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
168
c2cd64b5
JJ
169 if (!gcov_var.file)
170 {
171 close (fd);
172 return 0;
173 }
c2cd64b5 174#else
ca29da43 175 if (mode >= 0)
fc0911e0 176 /* Open an existing file. */
ad467730
NV
177 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
178
7d63a2fa 179 if (gcov_var.file)
fc0911e0 180 mode = 1;
7d63a2fa 181 else if (mode <= 0)
fc0911e0
NS
182 /* Create a new file. */
183 gcov_var.file = fopen (name, "w+b");
184
ca29da43
NS
185 if (!gcov_var.file)
186 return 0;
ca29da43
NS
187#endif
188
fc0911e0
NS
189 gcov_var.mode = mode ? mode : 1;
190
160e2e4f 191 return 1;
ca29da43
NS
192}
193
194/* Close the current gcov file. Flushes data to disk. Returns nonzero
195 on failure or error flag set. */
196
197GCOV_LINKAGE int
82a30d6f 198gcov_close (void)
ca29da43 199{
ca29da43
NS
200 if (gcov_var.file)
201 {
929f2cf4
VL
202 if (fclose (gcov_var.file))
203 gcov_var.error = 1;
204
ca29da43 205 gcov_var.file = 0;
ca29da43 206 }
7d63a2fa
NS
207 gcov_var.mode = 0;
208 return gcov_var.error;
209}
210
17d1594b 211#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
160e2e4f
NS
212/* Check if MAGIC is EXPECTED. Use it to determine endianness of the
213 file. Returns +1 for same endian, -1 for other endian and zero for
214 not EXPECTED. */
215
216GCOV_LINKAGE int
217gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
218{
219 if (magic == expected)
220 return 1;
23eb66d1 221
222 if (__builtin_bswap32 (magic) == expected)
160e2e4f
NS
223 {
224 gcov_var.endian = 1;
225 return -1;
226 }
227 return 0;
228}
229#endif
230
ca29da43 231#if !IN_GCOV
9124bbe1 232/* Write DATA of LENGTH characters to coverage file. */
ca29da43
NS
233
234GCOV_LINKAGE void
9124bbe1 235gcov_write (const void *data, unsigned length)
ca29da43 236{
9124bbe1 237 gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
23eb66d1 238 if (r != 1)
239 gcov_var.error = 1;
ca29da43
NS
240}
241
9124bbe1 242/* Write unsigned VALUE to coverage file. */
ca29da43 243
ca29da43 244GCOV_LINKAGE void
9124bbe1 245gcov_write_unsigned (gcov_unsigned_t value)
ca29da43 246{
9124bbe1
SH
247 gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
248 if (r != 1)
249 gcov_var.error = 1;
ca29da43 250}
ca29da43 251
796621e8 252#if !IN_LIBGCOV
ca29da43
NS
253/* Write STRING to coverage file. Sets error flag on file
254 error, overflow flag on overflow */
255
256GCOV_LINKAGE void
257gcov_write_string (const char *string)
258{
259 unsigned length = 0;
ca29da43
NS
260
261 if (string)
23eb66d1 262 length = strlen (string) + 1;
7d63a2fa 263
23eb66d1 264 gcov_write_unsigned (length);
265 if (length > 0)
0040ecb0 266 {
23eb66d1 267 gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
268 if (r != 1)
269 gcov_var.error = 1;
0040ecb0 270 }
ca29da43 271}
796621e8 272#endif
ca29da43 273
676519f7
BE
274#if !IN_LIBGCOV
275/* Write FILENAME to coverage file. Sets error flag on file
276 error, overflow flag on overflow */
277
278GCOV_LINKAGE void
279gcov_write_filename (const char *filename)
280{
281 if (profile_abs_path_flag && filename && filename[0]
282 && !(IS_DIR_SEPARATOR (filename[0])
283#if HAVE_DOS_BASED_FILE_SYSTEM
284 || filename[1] == ':'
285#endif
286 ))
287 {
288 char *buf = getcwd (NULL, 0);
289 if (buf != NULL && buf[0])
290 {
291 size_t len = strlen (buf);
292 buf = (char*)xrealloc (buf, len + strlen (filename) + 2);
293 if (!IS_DIR_SEPARATOR (buf[len - 1]))
294 strcat (buf, "/");
295 strcat (buf, filename);
296 gcov_write_string (buf);
297 free (buf);
298 return;
299 }
300 }
301
302 gcov_write_string (filename);
303}
304#endif
305
23eb66d1 306/* Move to a given position in a gcov file. */
307
308GCOV_LINKAGE void
309gcov_seek (gcov_position_t base)
310{
311 fseek (gcov_var.file, base, SEEK_SET);
312}
313
474f141e 314#if !IN_LIBGCOV
ca29da43
NS
315/* Write a tag TAG and reserve space for the record length. Return a
316 value to be used for gcov_write_length. */
317
9b514d25
NS
318GCOV_LINKAGE gcov_position_t
319gcov_write_tag (gcov_unsigned_t tag)
ca29da43 320{
23eb66d1 321 gcov_position_t result = gcov_position ();
322 gcov_write_unsigned (tag);
323 gcov_write_unsigned (0);
b8698a0f 324
ca29da43
NS
325 return result;
326}
327
328/* Write a record length using POSITION, which was returned by
329 gcov_write_tag. The current file position is the end of the
330 record, and is restored before returning. Returns nonzero on
331 overflow. */
332
333GCOV_LINKAGE void
9b514d25 334gcov_write_length (gcov_position_t position)
ca29da43 335{
23eb66d1 336 gcov_position_t current_position = gcov_position ();
e3f0315f 337 gcov_nonruntime_assert (gcov_var.mode < 0);
23eb66d1 338 gcov_nonruntime_assert (current_position >= position + 2 * GCOV_WORD_SIZE);
339
340 gcov_seek (position + GCOV_WORD_SIZE);
341 gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE);
342 gcov_seek (current_position);
ca29da43 343}
9b514d25
NS
344
345#else /* IN_LIBGCOV */
474f141e 346
9124bbe1 347/* Write a summary structure to the gcov file. */
ca29da43
NS
348
349GCOV_LINKAGE void
9b514d25 350gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
ca29da43 351{
9124bbe1
SH
352 gcov_write_unsigned (tag);
353 gcov_write_unsigned (GCOV_TAG_SUMMARY_LENGTH);
7f3577f5 354 gcov_write_unsigned (summary->runs);
512cc015 355 gcov_write_unsigned (summary->sum_max);
ca29da43 356}
512cc015 357
ca29da43
NS
358#endif /* IN_LIBGCOV */
359
360#endif /*!IN_GCOV */
361
23eb66d1 362/* Return a pointer to read COUNT bytes from the gcov file. Returns
71c0e7fc 363 NULL on failure (read past EOF). */
ca29da43 364
23eb66d1 365static void *
366gcov_read_bytes (void *buffer, unsigned count)
ca29da43 367{
1bba63a7
AK
368 if (gcov_var.mode <= 0)
369 return NULL;
370
23eb66d1 371 unsigned read = fread (buffer, count, 1, gcov_var.file);
372 if (read != 1)
373 return NULL;
374
375 return buffer;
376}
377
378/* Read WORDS gcov_unsigned_t values from gcov file. */
379
380static gcov_unsigned_t *
381gcov_read_words (void *buffer, unsigned words)
382{
383 return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
ca29da43
NS
384}
385
386/* Read unsigned value from a coverage file. Sets error flag on file
387 error, overflow flag on overflow */
388
9b514d25 389GCOV_LINKAGE gcov_unsigned_t
82a30d6f 390gcov_read_unsigned (void)
ca29da43 391{
160e2e4f 392 gcov_unsigned_t value;
23eb66d1 393 gcov_unsigned_t allocated_buffer[1];
394 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
ca29da43
NS
395
396 if (!buffer)
397 return 0;
23eb66d1 398
160e2e4f 399 value = from_file (buffer[0]);
ca29da43
NS
400 return value;
401}
402
403/* Read counter value from a coverage file. Sets error flag on file
404 error, overflow flag on overflow */
405
406GCOV_LINKAGE gcov_type
82a30d6f 407gcov_read_counter (void)
ca29da43 408{
160e2e4f 409 gcov_type value;
23eb66d1 410 gcov_unsigned_t allocated_buffer[2];
411 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
ca29da43
NS
412
413 if (!buffer)
414 return 0;
160e2e4f
NS
415 value = from_file (buffer[0]);
416 if (sizeof (value) > sizeof (gcov_unsigned_t))
417 value |= ((gcov_type) from_file (buffer[1])) << 32;
418 else if (buffer[1])
419 gcov_var.error = -1;
6d9901e7 420
ca29da43
NS
421 return value;
422}
423
1f2bb38a
ML
424/* Mangle filename path of BASE and output new allocated pointer with
425 mangled path. */
426
427char *
428mangle_path (char const *base)
429{
430 /* Convert '/' to '#', convert '..' to '^',
431 convert ':' to '~' on DOS based file system. */
432 const char *probe;
12502bf2 433 char *buffer = (char *)xmalloc (strlen (base) + 1);
1f2bb38a
ML
434 char *ptr = buffer;
435
436#if HAVE_DOS_BASED_FILE_SYSTEM
437 if (base[0] && base[1] == ':')
438 {
439 ptr[0] = base[0];
440 ptr[1] = '~';
441 ptr += 2;
442 base += 2;
443 }
444#endif
445 for (; *base; base = probe)
446 {
447 size_t len;
448
449 for (probe = base; *probe; probe++)
450 if (*probe == '/')
451 break;
452 len = probe - base;
453 if (len == 2 && base[0] == '.' && base[1] == '.')
454 *ptr++ = '^';
455 else
456 {
457 memcpy (ptr, base, len);
458 ptr += len;
459 }
460 if (*probe)
461 {
462 *ptr++ = '#';
463 probe++;
464 }
465 }
466
467 /* Terminate the string. */
468 *ptr = '\0';
469
470 return buffer;
471}
472
c77556a5
RX
473/* We need to expose the below function when compiling for gcov-tool. */
474
475#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
ca29da43
NS
476/* Read string from coverage file. Returns a pointer to a static
477 buffer, or NULL on empty string. You must copy the string before
478 calling another gcov function. */
479
480GCOV_LINKAGE const char *
82a30d6f 481gcov_read_string (void)
ca29da43
NS
482{
483 unsigned length = gcov_read_unsigned ();
b8698a0f 484
ca29da43
NS
485 if (!length)
486 return 0;
487
23eb66d1 488 void *buffer = XNEWVEC (char *, length);
489 return (const char *) gcov_read_bytes (buffer, length);
ca29da43 490}
796621e8 491#endif
ca29da43
NS
492
493GCOV_LINKAGE void
494gcov_read_summary (struct gcov_summary *summary)
495{
7f3577f5 496 summary->runs = gcov_read_unsigned ();
512cc015 497 summary->sum_max = gcov_read_unsigned ();
ca29da43
NS
498}
499
c77556a5
RX
500/* We need to expose the below function when compiling for gcov-tool. */
501
502#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
7d63a2fa
NS
503/* Reset to a known position. BASE should have been obtained from
504 gcov_position, LENGTH should be a record length. */
505
506GCOV_LINKAGE void
507gcov_sync (gcov_position_t base, gcov_unsigned_t length)
508{
e3f0315f 509 gcov_nonruntime_assert (gcov_var.mode > 0);
7d63a2fa 510 base += length;
23eb66d1 511 fseek (gcov_var.file, base, SEEK_SET);
7d63a2fa
NS
512}
513#endif
514
ca29da43
NS
515#if IN_GCOV > 0
516/* Return the modification time of the current gcov file. */
517
518GCOV_LINKAGE time_t
82a30d6f 519gcov_time (void)
ca29da43
NS
520{
521 struct stat status;
b8698a0f 522
ca29da43
NS
523 if (fstat (fileno (gcov_var.file), &status))
524 return 0;
525 else
526 return status.st_mtime;
527}
528#endif /* IN_GCOV */