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 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 10Software Foundation; either version 3, or (at your option) any later 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 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 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 30static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned); 31 32struct 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 43static inline 44#endif 45gcov_position_t 46gcov_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 54static inline 55#endif 56int 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{ 67 gcov_var.mode = -1; 68 fseek (gcov_var.file, 0L, SEEK_SET); 69} 70#endif 71 72static inline gcov_unsigned_t 73from_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 91GCOV_LINKAGE int 92#if IN_LIBGCOV 93gcov_open (const char *name) 94#else 95gcov_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 197GCOV_LINKAGE int 198gcov_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 216GCOV_LINKAGE int 217gcov_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 234GCOV_LINKAGE void 235gcov_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 244GCOV_LINKAGE void 245gcov_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 256GCOV_LINKAGE void 257gcov_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 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 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 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 318GCOV_LINKAGE gcov_position_t 319gcov_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 333GCOV_LINKAGE void 334gcov_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 349GCOV_LINKAGE void 350gcov_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 365static void * 366gcov_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 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); 384} 385 386/* Read unsigned value from a coverage file. Sets error flag on file 387 error, overflow flag on overflow */ 388 389GCOV_LINKAGE gcov_unsigned_t 390gcov_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 406GCOV_LINKAGE gcov_type 407gcov_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 427char * 428mangle_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 480GCOV_LINKAGE const char * 481gcov_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 493GCOV_LINKAGE void 494gcov_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 506GCOV_LINKAGE void 507gcov_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 518GCOV_LINKAGE time_t 519gcov_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