]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/gcov-io.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / gcov-io.c
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>.
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 static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned);
31
32 struct gcov_var
33 {
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;
39
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)
47 {
48 return ftell (gcov_var.file);
49 }
50
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)
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. */
64 GCOV_LINKAGE inline void
65 gcov_rewrite (void)
66 {
67 gcov_var.mode = -1;
68 fseek (gcov_var.file, 0L, SEEK_SET);
69 }
70 #endif
71
72 static inline gcov_unsigned_t
73 from_file (gcov_unsigned_t value)
74 {
75 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
76 if (gcov_var.endian)
77 return __builtin_bswap32 (value);
78 #endif
79 return value;
80 }
81
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. */
90
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
97 {
98 #if IN_LIBGCOV
99 int mode = 0;
100 #endif
101 #if GCOV_LOCKED
102 struct flock s_flock;
103 int fd;
104
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
112
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)
120 {
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);
125 }
126 else
127 {
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);
132 }
133 if (fd < 0)
134 return 0;
135
136 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
137 continue;
138
139 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
140
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
169 if (!gcov_var.file)
170 {
171 close (fd);
172 return 0;
173 }
174 #else
175 if (mode >= 0)
176 /* Open an existing file. */
177 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
178
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");
184
185 if (!gcov_var.file)
186 return 0;
187 #endif
188
189 gcov_var.mode = mode ? mode : 1;
190
191 return 1;
192 }
193
194 /* Close the current gcov file. Flushes data to disk. Returns nonzero
195 on failure or error flag set. */
196
197 GCOV_LINKAGE int
198 gcov_close (void)
199 {
200 if (gcov_var.file)
201 {
202 if (fclose (gcov_var.file))
203 gcov_var.error = 1;
204
205 gcov_var.file = 0;
206 }
207 gcov_var.mode = 0;
208 return gcov_var.error;
209 }
210
211 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
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
216 GCOV_LINKAGE int
217 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
218 {
219 if (magic == expected)
220 return 1;
221
222 if (__builtin_bswap32 (magic) == expected)
223 {
224 gcov_var.endian = 1;
225 return -1;
226 }
227 return 0;
228 }
229 #endif
230
231 #if !IN_GCOV
232 /* Write DATA of LENGTH characters to coverage file. */
233
234 GCOV_LINKAGE void
235 gcov_write (const void *data, unsigned length)
236 {
237 gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
238 if (r != 1)
239 gcov_var.error = 1;
240 }
241
242 /* Write unsigned VALUE to coverage file. */
243
244 GCOV_LINKAGE void
245 gcov_write_unsigned (gcov_unsigned_t value)
246 {
247 gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
248 if (r != 1)
249 gcov_var.error = 1;
250 }
251
252 #if !IN_LIBGCOV
253 /* Write STRING to coverage file. Sets error flag on file
254 error, overflow flag on overflow */
255
256 GCOV_LINKAGE void
257 gcov_write_string (const char *string)
258 {
259 unsigned length = 0;
260
261 if (string)
262 length = strlen (string) + 1;
263
264 gcov_write_unsigned (length);
265 if (length > 0)
266 {
267 gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
268 if (r != 1)
269 gcov_var.error = 1;
270 }
271 }
272 #endif
273
274 #if !IN_LIBGCOV
275 /* Write FILENAME to coverage file. Sets error flag on file
276 error, overflow flag on overflow */
277
278 GCOV_LINKAGE void
279 gcov_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
306 /* Move to a given position in a gcov file. */
307
308 GCOV_LINKAGE void
309 gcov_seek (gcov_position_t base)
310 {
311 fseek (gcov_var.file, base, SEEK_SET);
312 }
313
314 #if !IN_LIBGCOV
315 /* Write a tag TAG and reserve space for the record length. Return a
316 value to be used for gcov_write_length. */
317
318 GCOV_LINKAGE gcov_position_t
319 gcov_write_tag (gcov_unsigned_t tag)
320 {
321 gcov_position_t result = gcov_position ();
322 gcov_write_unsigned (tag);
323 gcov_write_unsigned (0);
324
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
333 GCOV_LINKAGE void
334 gcov_write_length (gcov_position_t position)
335 {
336 gcov_position_t current_position = gcov_position ();
337 gcov_nonruntime_assert (gcov_var.mode < 0);
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);
343 }
344
345 #else /* IN_LIBGCOV */
346
347 /* Write a summary structure to the gcov file. */
348
349 GCOV_LINKAGE void
350 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
351 {
352 gcov_write_unsigned (tag);
353 gcov_write_unsigned (GCOV_TAG_SUMMARY_LENGTH);
354 gcov_write_unsigned (summary->runs);
355 gcov_write_unsigned (summary->sum_max);
356 }
357
358 #endif /* IN_LIBGCOV */
359
360 #endif /*!IN_GCOV */
361
362 /* Return a pointer to read COUNT bytes from the gcov file. Returns
363 NULL on failure (read past EOF). */
364
365 static void *
366 gcov_read_bytes (void *buffer, unsigned count)
367 {
368 if (gcov_var.mode <= 0)
369 return NULL;
370
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
380 static gcov_unsigned_t *
381 gcov_read_words (void *buffer, unsigned words)
382 {
383 return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
384 }
385
386 /* Read unsigned value from a coverage file. Sets error flag on file
387 error, overflow flag on overflow */
388
389 GCOV_LINKAGE gcov_unsigned_t
390 gcov_read_unsigned (void)
391 {
392 gcov_unsigned_t value;
393 gcov_unsigned_t allocated_buffer[1];
394 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
395
396 if (!buffer)
397 return 0;
398
399 value = from_file (buffer[0]);
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
406 GCOV_LINKAGE gcov_type
407 gcov_read_counter (void)
408 {
409 gcov_type value;
410 gcov_unsigned_t allocated_buffer[2];
411 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
412
413 if (!buffer)
414 return 0;
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;
420
421 return value;
422 }
423
424 /* Mangle filename path of BASE and output new allocated pointer with
425 mangled path. */
426
427 char *
428 mangle_path (char const *base)
429 {
430 /* Convert '/' to '#', convert '..' to '^',
431 convert ':' to '~' on DOS based file system. */
432 const char *probe;
433 char *buffer = (char *)xmalloc (strlen (base) + 1);
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
473 /* We need to expose the below function when compiling for gcov-tool. */
474
475 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
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
480 GCOV_LINKAGE const char *
481 gcov_read_string (void)
482 {
483 unsigned length = gcov_read_unsigned ();
484
485 if (!length)
486 return 0;
487
488 void *buffer = XNEWVEC (char *, length);
489 return (const char *) gcov_read_bytes (buffer, length);
490 }
491 #endif
492
493 GCOV_LINKAGE void
494 gcov_read_summary (struct gcov_summary *summary)
495 {
496 summary->runs = gcov_read_unsigned ();
497 summary->sum_max = gcov_read_unsigned ();
498 }
499
500 /* We need to expose the below function when compiling for gcov-tool. */
501
502 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
503 /* Reset to a known position. BASE should have been obtained from
504 gcov_position, LENGTH should be a record length. */
505
506 GCOV_LINKAGE void
507 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
508 {
509 gcov_nonruntime_assert (gcov_var.mode > 0);
510 base += length;
511 fseek (gcov_var.file, base, SEEK_SET);
512 }
513 #endif
514
515 #if IN_GCOV > 0
516 /* Return the modification time of the current gcov file. */
517
518 GCOV_LINKAGE time_t
519 gcov_time (void)
520 {
521 struct stat status;
522
523 if (fstat (fileno (gcov_var.file), &status))
524 return 0;
525 else
526 return status.st_mtime;
527 }
528 #endif /* IN_GCOV */