xref: /netbsd-src/external/gpl3/gcc/dist/gcc/gcov-io.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* File format for coverage information
2    Copyright (C) 1996-2022 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
gcov_position(void)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
gcov_is_error(void)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
gcov_rewrite(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
from_file(gcov_unsigned_t value)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
gcov_open(const char * name)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
gcov_close(void)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
gcov_magic(gcov_unsigned_t magic,gcov_unsigned_t expected)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
gcov_write(const void * data,unsigned length)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
gcov_write_unsigned(gcov_unsigned_t value)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
gcov_write_string(const char * string)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
gcov_write_filename(const char * filename)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
gcov_seek(gcov_position_t base)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
gcov_write_tag(gcov_unsigned_t tag)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
gcov_write_length(gcov_position_t position)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
gcov_write_summary(gcov_unsigned_t tag,const struct gcov_summary * summary)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 *
gcov_read_bytes(void * buffer,unsigned count)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 *
gcov_read_words(void * buffer,unsigned words)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
gcov_read_unsigned(void)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
gcov_read_counter(void)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 *
mangle_path(char const * base)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.  Allocate the buffer for the string
477    from the heap or die.  Return a pointer to the string, or NULL on
478    empty string.  */
479 
480 GCOV_LINKAGE const char *
gcov_read_string(void)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
gcov_read_summary(struct gcov_summary * summary)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
gcov_sync(gcov_position_t base,gcov_unsigned_t length)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
gcov_time(void)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 */
529