]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/gcov-io.cc
Update gcc .po files
[thirdparty/gcc.git] / gcc / gcov-io.cc
1 /* File format for coverage information
2 Copyright (C) 1996-2023 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 /* Indicates the last gcov file access error or that no error occurred
33 so far. */
34 enum gcov_file_error
35 {
36 GCOV_FILE_COUNTER_OVERFLOW = -1,
37 GCOV_FILE_NO_ERROR = 0,
38 GCOV_FILE_WRITE_ERROR = 1,
39 GCOV_FILE_EOF = 2
40 };
41
42 struct gcov_var
43 {
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;
52
53 #define GCOV_MODE_STDIN 2
54
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)
62 {
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);
68 }
69
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)
77 {
78 return gcov_var.file ? gcov_var.error : 1;
79 }
80
81 #if IN_LIBGCOV
82 /* Move to beginning of file, initialize for writing, and clear file error
83 status. */
84
85 GCOV_LINKAGE inline void
86 gcov_rewrite (void)
87 {
88 gcov_var.mode = -1;
89 gcov_var.error = GCOV_FILE_NO_ERROR;
90 fseek (gcov_var.file, 0L, SEEK_SET);
91 }
92 #endif
93
94 static inline gcov_unsigned_t
95 from_file (gcov_unsigned_t value)
96 {
97 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
98 if (gcov_var.endian)
99 return __builtin_bswap32 (value);
100 #endif
101 return value;
102 }
103
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. */
112
113 GCOV_LINKAGE int
114 gcov_open (const char *name, int mode)
115 {
116 #if GCOV_LOCKED
117 struct flock s_flock;
118 int fd;
119
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
127
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)
136 {
137 gcov_nonruntime_assert (gcov_var.mode > 0);
138 gcov_var.file = stdin;
139 gcov_var.mode = GCOV_MODE_STDIN;
140 return 1;
141 }
142 #endif
143 #if GCOV_LOCKED
144 if (mode > 0)
145 {
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);
150 }
151 else
152 {
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);
157 }
158 if (fd < 0)
159 return 0;
160
161 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
162 continue;
163
164 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
165
166 if (!gcov_var.file)
167 {
168 close (fd);
169 return 0;
170 }
171 #elif GCOV_LOCKED_WITH_LOCKING
172 if (mode > 0)
173 {
174 /* pass mode (ignored) for compatibility */
175 fd = open (name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
176 }
177 else
178 {
179 /* Truncate if force new mode. */
180 fd = open (name, O_RDWR | O_BINARY | O_CREAT | (mode < 0 ? O_TRUNC : 0),
181 0666);
182 }
183 if (fd < 0)
184 return 0;
185
186 if (_locking (fd, _LK_LOCK, LONG_MAX) < 0)
187 {
188 close (fd);
189 return 0;
190 }
191
192 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
193
194 if (!gcov_var.file)
195 {
196 close (fd);
197 return 0;
198 }
199 #else
200 if (mode >= 0)
201 /* Open an existing file. */
202 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
203
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");
209
210 if (!gcov_var.file)
211 return 0;
212 #endif
213
214 gcov_var.mode = mode ? mode : 1;
215
216 return 1;
217 }
218
219 /* Close the current gcov file. Flushes data to disk. Returns nonzero
220 on failure or error flag set. */
221
222 GCOV_LINKAGE int
223 gcov_close (void)
224 {
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)
231 {
232 if (fclose (gcov_var.file))
233 gcov_var.error = GCOV_FILE_WRITE_ERROR;
234
235 gcov_var.file = 0;
236 }
237 gcov_var.mode = 0;
238 return gcov_var.error;
239 }
240
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. */
245
246 GCOV_LINKAGE int
247 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
248 {
249 if (magic == expected)
250 return 1;
251
252 if (__builtin_bswap32 (magic) == expected)
253 {
254 gcov_var.endian = 1;
255 return -1;
256 }
257 return 0;
258 }
259 #endif
260
261 #if !IN_GCOV
262 /* Write DATA of LENGTH characters to coverage file. */
263
264 GCOV_LINKAGE void
265 gcov_write (const void *data, unsigned length)
266 {
267 gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
268 if (r != 1)
269 gcov_var.error = GCOV_FILE_WRITE_ERROR;
270 }
271
272 /* Write unsigned VALUE to coverage file. */
273
274 GCOV_LINKAGE void
275 gcov_write_unsigned (gcov_unsigned_t value)
276 {
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;
280 }
281
282 #if !IN_LIBGCOV
283 /* Write STRING to coverage file. Sets error flag on file
284 error, overflow flag on overflow */
285
286 GCOV_LINKAGE void
287 gcov_write_string (const char *string)
288 {
289 unsigned length = 0;
290
291 if (string)
292 length = strlen (string) + 1;
293
294 gcov_write_unsigned (length);
295 if (length > 0)
296 {
297 gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
298 if (r != 1)
299 gcov_var.error = GCOV_FILE_WRITE_ERROR;
300 }
301 }
302 #endif
303
304 #if !IN_LIBGCOV
305 /* Write FILENAME to coverage file. Sets error flag on file
306 error, overflow flag on overflow */
307
308 GCOV_LINKAGE void
309 gcov_write_filename (const char *filename)
310 {
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
316 ))
317 {
318 char *buf = getcwd (NULL, 0);
319 if (buf != NULL && buf[0])
320 {
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;
329 }
330 }
331
332 gcov_write_string (filename);
333 }
334
335 /* Move to a given position in a gcov file. */
336
337 static void
338 gcov_seek (gcov_position_t base)
339 {
340 fseek (gcov_var.file, base, SEEK_SET);
341 }
342
343 /* Write a tag TAG and reserve space for the record length. Return a
344 value to be used for gcov_write_length. */
345
346 GCOV_LINKAGE gcov_position_t
347 gcov_write_tag (gcov_unsigned_t tag)
348 {
349 gcov_position_t result = gcov_position ();
350 gcov_write_unsigned (tag);
351 gcov_write_unsigned (0);
352
353 return result;
354 }
355
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. */
360
361 GCOV_LINKAGE void
362 gcov_write_length (gcov_position_t position)
363 {
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);
367
368 gcov_seek (position + GCOV_WORD_SIZE);
369 gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE);
370 gcov_seek (current_position);
371 }
372
373 #else /* IN_LIBGCOV */
374
375 /* Write an object summary structure to the gcov file. */
376
377 GCOV_LINKAGE void
378 gcov_write_object_summary (const struct gcov_summary *summary)
379 {
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);
384 }
385
386 #endif /* IN_LIBGCOV */
387
388 #endif /*!IN_GCOV */
389
390 /* Return a pointer to read COUNT bytes from the gcov file. Returns
391 NULL on failure (read past EOF). */
392
393 static void *
394 gcov_read_bytes (void *buffer, unsigned count)
395 {
396 if (gcov_var.mode <= 0)
397 return NULL;
398
399 unsigned read = fread (buffer, count, 1, gcov_var.file);
400 if (read != 1)
401 {
402 if (feof (gcov_var.file))
403 gcov_var.error = GCOV_FILE_EOF;
404 return NULL;
405 }
406
407 #ifdef IN_GCOV_TOOL
408 gcov_var.pos += count;
409 #endif
410 return buffer;
411 }
412
413 /* Read WORDS gcov_unsigned_t values from gcov file. */
414
415 static gcov_unsigned_t *
416 gcov_read_words (void *buffer, unsigned words)
417 {
418 return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
419 }
420
421 /* Read unsigned value from a coverage file. Sets error flag on file
422 error, overflow flag on overflow */
423
424 GCOV_LINKAGE gcov_unsigned_t
425 gcov_read_unsigned (void)
426 {
427 gcov_unsigned_t value;
428 gcov_unsigned_t allocated_buffer[1];
429 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
430
431 if (!buffer)
432 return 0;
433
434 value = from_file (buffer[0]);
435 return value;
436 }
437
438 /* Read counter value from a coverage file. Sets error flag on file
439 error, overflow flag on overflow */
440
441 GCOV_LINKAGE gcov_type
442 gcov_read_counter (void)
443 {
444 gcov_type value;
445 gcov_unsigned_t allocated_buffer[2];
446 gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
447
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;
455
456 return value;
457 }
458
459 /* Mangle filename path of BASE and output new allocated pointer with
460 mangled path. */
461
462 char *
463 mangle_path (char const *base)
464 {
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;
470
471 #if HAVE_DOS_BASED_FILE_SYSTEM
472 if (base[0] && base[1] == ':')
473 {
474 ptr[0] = base[0];
475 ptr[1] = '~';
476 ptr += 2;
477 base += 2;
478 }
479 #endif
480 for (; *base; base = probe)
481 {
482 size_t len;
483
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
491 {
492 memcpy (ptr, base, len);
493 ptr += len;
494 }
495 if (*probe)
496 {
497 *ptr++ = '#';
498 probe++;
499 }
500 }
501
502 /* Terminate the string. */
503 *ptr = '\0';
504
505 return buffer;
506 }
507
508 /* We need to expose the below function when compiling for gcov-tool. */
509
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. */
514
515 GCOV_LINKAGE const char *
516 gcov_read_string (void)
517 {
518 unsigned length = gcov_read_unsigned ();
519
520 if (!length)
521 return 0;
522
523 void *buffer = XNEWVEC (char *, length);
524 return (const char *) gcov_read_bytes (buffer, length);
525 }
526 #endif
527
528 GCOV_LINKAGE void
529 gcov_read_summary (struct gcov_summary *summary)
530 {
531 summary->runs = gcov_read_unsigned ();
532 summary->sum_max = gcov_read_unsigned ();
533 }
534
535 /* We need to expose the below function when compiling for gcov-tool. */
536
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. */
540
541 GCOV_LINKAGE void
542 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
543 {
544 gcov_nonruntime_assert (gcov_var.mode > 0);
545 base += length;
546 #ifdef IN_GCOV_TOOL
547 if (gcov_var.mode == GCOV_MODE_STDIN)
548 {
549 while (gcov_var.pos < base)
550 {
551 ++gcov_var.pos;
552 (void)fgetc (gcov_var.file);
553 }
554 return;
555 }
556 #endif
557 fseek (gcov_var.file, base, SEEK_SET);
558 }
559 #endif
560
561 #if IN_GCOV > 0
562 /* Return the modification time of the current gcov file. */
563
564 GCOV_LINKAGE time_t
565 gcov_time (void)
566 {
567 struct stat status;
568
569 if (fstat (fileno (gcov_var.file), &status))
570 return 0;
571 else
572 return status.st_mtime;
573 }
574 #endif /* IN_GCOV */