1/* File format for coverage information 2 Copyright (C) 1996, 1997, 1998, 2000, 2002, 2003, 2004, 2005, 2007, 3 2008 Free Software Foundation, Inc. 4 Contributed by Bob Manson <manson@cygnus.com>. 5 Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. 6 7This file is part of GCC. 8 9GCC is free software; you can redistribute it and/or modify it under 10the terms of the GNU General Public License as published by the Free 11Software Foundation; either version 3, or (at your option) any later 12version. 13 14GCC is distributed in the hope that it will be useful, but WITHOUT ANY 15WARRANTY; without even the implied warranty of MERCHANTABILITY or 16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17for more details. 18 19Under Section 7 of GPL version 3, you are granted additional 20permissions described in the GCC Runtime Library Exception, version 213.1, as published by the Free Software Foundation. 22 23You should have received a copy of the GNU General Public License and 24a copy of the GCC Runtime Library Exception along with this program; 25see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 26<http://www.gnu.org/licenses/>. */ 27 28/* Routines declared in gcov-io.h. This file should be #included by 29 another source file, after having #included gcov-io.h. */ 30 31#if !IN_GCOV 32static void gcov_write_block (unsigned); 33static gcov_unsigned_t *gcov_write_words (unsigned); 34#endif 35static const gcov_unsigned_t *gcov_read_words (unsigned); 36#if !IN_LIBGCOV 37static void gcov_allocate (unsigned); 38#endif 39 40static inline gcov_unsigned_t from_file (gcov_unsigned_t value) 41{ 42#if !IN_LIBGCOV 43 if (gcov_var.endian) 44 { 45 value = (value >> 16) | (value << 16); 46 value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff); 47 } 48#endif 49 return value; 50} 51 52/* Open a gcov file. NAME is the name of the file to open and MODE 53 indicates whether a new file should be created, or an existing file 54 opened. If MODE is >= 0 an existing file will be opened, if 55 possible, and if MODE is <= 0, a new file will be created. Use 56 MODE=0 to attempt to reopen an existing file and then fall back on 57 creating a new one. If MODE < 0, the file will be opened in 58 read-only mode. Otherwise it will be opened for modification. 59 Return zero on failure, >0 on opening an existing file and <0 on 60 creating a new one. */ 61 62GCOV_LINKAGE int 63#if IN_LIBGCOV 64gcov_open (const char *name) 65#else 66gcov_open (const char *name, int mode) 67#endif 68{ 69#if IN_LIBGCOV 70 const int mode = 0; 71#endif 72#if GCOV_LOCKED 73 struct flock s_flock; 74 int fd; 75 76 s_flock.l_whence = SEEK_SET; 77 s_flock.l_start = 0; 78 s_flock.l_len = 0; /* Until EOF. */ 79 s_flock.l_pid = getpid (); 80#endif 81 82 gcc_assert (!gcov_var.file); 83 gcov_var.start = 0; 84 gcov_var.offset = gcov_var.length = 0; 85 gcov_var.overread = -1u; 86 gcov_var.error = 0; 87#if !IN_LIBGCOV 88 gcov_var.endian = 0; 89#endif 90#if GCOV_LOCKED 91 if (mode > 0) 92 { 93 /* Read-only mode - acquire a read-lock. */ 94 s_flock.l_type = F_RDLCK; 95 fd = open (name, O_RDONLY); 96 } 97 else 98 { 99 /* Write mode - acquire a write-lock. */ 100 s_flock.l_type = F_WRLCK; 101 fd = open (name, O_RDWR | O_CREAT, 0666); 102 } 103 if (fd < 0) 104 return 0; 105 106 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR) 107 continue; 108 109 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b"); 110 111 if (!gcov_var.file) 112 { 113 close (fd); 114 return 0; 115 } 116 117 if (mode > 0) 118 gcov_var.mode = 1; 119 else if (mode == 0) 120 { 121 struct stat st; 122 123 if (fstat (fd, &st) < 0) 124 { 125 fclose (gcov_var.file); 126 gcov_var.file = 0; 127 return 0; 128 } 129 if (st.st_size != 0) 130 gcov_var.mode = 1; 131 else 132 gcov_var.mode = mode * 2 + 1; 133 } 134 else 135 gcov_var.mode = mode * 2 + 1; 136#else 137 if (mode >= 0) 138 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b"); 139 140 if (gcov_var.file) 141 gcov_var.mode = 1; 142 else if (mode <= 0) 143 { 144 gcov_var.file = fopen (name, "w+b"); 145 if (gcov_var.file) 146 gcov_var.mode = mode * 2 + 1; 147 } 148 if (!gcov_var.file) 149 return 0; 150#endif 151 152 setbuf (gcov_var.file, (char *)0); 153 154 return 1; 155} 156 157/* Close the current gcov file. Flushes data to disk. Returns nonzero 158 on failure or error flag set. */ 159 160GCOV_LINKAGE int 161gcov_close (void) 162{ 163 if (gcov_var.file) 164 { 165#if !IN_GCOV 166 if (gcov_var.offset && gcov_var.mode < 0) 167 gcov_write_block (gcov_var.offset); 168#endif 169 fclose (gcov_var.file); 170 gcov_var.file = 0; 171 gcov_var.length = 0; 172 } 173#if !IN_LIBGCOV 174 free (gcov_var.buffer); 175 gcov_var.alloc = 0; 176 gcov_var.buffer = 0; 177#endif 178 gcov_var.mode = 0; 179 return gcov_var.error; 180} 181 182#if !IN_LIBGCOV 183/* Check if MAGIC is EXPECTED. Use it to determine endianness of the 184 file. Returns +1 for same endian, -1 for other endian and zero for 185 not EXPECTED. */ 186 187GCOV_LINKAGE int 188gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected) 189{ 190 if (magic == expected) 191 return 1; 192 magic = (magic >> 16) | (magic << 16); 193 magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff); 194 if (magic == expected) 195 { 196 gcov_var.endian = 1; 197 return -1; 198 } 199 return 0; 200} 201#endif 202 203#if !IN_LIBGCOV 204static void 205gcov_allocate (unsigned length) 206{ 207 size_t new_size = gcov_var.alloc; 208 209 if (!new_size) 210 new_size = GCOV_BLOCK_SIZE; 211 new_size += length; 212 new_size *= 2; 213 214 gcov_var.alloc = new_size; 215 gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2); 216} 217#endif 218 219#if !IN_GCOV 220/* Write out the current block, if needs be. */ 221 222static void 223gcov_write_block (unsigned size) 224{ 225 if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1) 226 gcov_var.error = 1; 227 gcov_var.start += size; 228 gcov_var.offset -= size; 229} 230 231/* Allocate space to write BYTES bytes to the gcov file. Return a 232 pointer to those bytes, or NULL on failure. */ 233 234static gcov_unsigned_t * 235gcov_write_words (unsigned words) 236{ 237 gcov_unsigned_t *result; 238 239 gcc_assert (gcov_var.mode < 0); 240#if IN_LIBGCOV 241 if (gcov_var.offset >= GCOV_BLOCK_SIZE) 242 { 243 gcov_write_block (GCOV_BLOCK_SIZE); 244 if (gcov_var.offset) 245 { 246 gcc_assert (gcov_var.offset == 1); 247 memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4); 248 } 249 } 250#else 251 if (gcov_var.offset + words > gcov_var.alloc) 252 gcov_allocate (gcov_var.offset + words); 253#endif 254 result = &gcov_var.buffer[gcov_var.offset]; 255 gcov_var.offset += words; 256 257 return result; 258} 259 260/* Write unsigned VALUE to coverage file. Sets error flag 261 appropriately. */ 262 263GCOV_LINKAGE void 264gcov_write_unsigned (gcov_unsigned_t value) 265{ 266 gcov_unsigned_t *buffer = gcov_write_words (1); 267 268 buffer[0] = value; 269} 270 271/* Write counter VALUE to coverage file. Sets error flag 272 appropriately. */ 273 274#if IN_LIBGCOV 275GCOV_LINKAGE void 276gcov_write_counter (gcov_type value) 277{ 278 gcov_unsigned_t *buffer = gcov_write_words (2); 279 280 buffer[0] = (gcov_unsigned_t) value; 281 if (sizeof (value) > sizeof (gcov_unsigned_t)) 282 buffer[1] = (gcov_unsigned_t) (value >> 32); 283 else 284 buffer[1] = 0; 285} 286#endif /* IN_LIBGCOV */ 287 288#if !IN_LIBGCOV 289/* Write STRING to coverage file. Sets error flag on file 290 error, overflow flag on overflow */ 291 292GCOV_LINKAGE void 293gcov_write_string (const char *string) 294{ 295 unsigned length = 0; 296 unsigned alloc = 0; 297 gcov_unsigned_t *buffer; 298 299 if (string) 300 { 301 length = strlen (string); 302 alloc = (length + 4) >> 2; 303 } 304 305 buffer = gcov_write_words (1 + alloc); 306 307 buffer[0] = alloc; 308 buffer[alloc] = 0; 309 memcpy (&buffer[1], string, length); 310} 311#endif 312 313#if !IN_LIBGCOV 314/* Write a tag TAG and reserve space for the record length. Return a 315 value to be used for gcov_write_length. */ 316 317GCOV_LINKAGE gcov_position_t 318gcov_write_tag (gcov_unsigned_t tag) 319{ 320 gcov_position_t result = gcov_var.start + gcov_var.offset; 321 gcov_unsigned_t *buffer = gcov_write_words (2); 322 323 buffer[0] = tag; 324 buffer[1] = 0; 325 326 return result; 327} 328 329/* Write a record length using POSITION, which was returned by 330 gcov_write_tag. The current file position is the end of the 331 record, and is restored before returning. Returns nonzero on 332 overflow. */ 333 334GCOV_LINKAGE void 335gcov_write_length (gcov_position_t position) 336{ 337 unsigned offset; 338 gcov_unsigned_t length; 339 gcov_unsigned_t *buffer; 340 341 gcc_assert (gcov_var.mode < 0); 342 gcc_assert (position + 2 <= gcov_var.start + gcov_var.offset); 343 gcc_assert (position >= gcov_var.start); 344 offset = position - gcov_var.start; 345 length = gcov_var.offset - offset - 2; 346 buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset]; 347 buffer[1] = length; 348 if (gcov_var.offset >= GCOV_BLOCK_SIZE) 349 gcov_write_block (gcov_var.offset); 350} 351 352#else /* IN_LIBGCOV */ 353 354/* Write a tag TAG and length LENGTH. */ 355 356GCOV_LINKAGE void 357gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length) 358{ 359 gcov_unsigned_t *buffer = gcov_write_words (2); 360 361 buffer[0] = tag; 362 buffer[1] = length; 363} 364 365/* Write a summary structure to the gcov file. Return nonzero on 366 overflow. */ 367 368GCOV_LINKAGE void 369gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) 370{ 371 unsigned ix; 372 const struct gcov_ctr_summary *csum; 373 374 gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH); 375 gcov_write_unsigned (summary->checksum); 376 for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) 377 { 378 gcov_write_unsigned (csum->num); 379 gcov_write_unsigned (csum->runs); 380 gcov_write_counter (csum->sum_all); 381 gcov_write_counter (csum->run_max); 382 gcov_write_counter (csum->sum_max); 383 } 384} 385#endif /* IN_LIBGCOV */ 386 387#endif /*!IN_GCOV */ 388 389/* Return a pointer to read BYTES bytes from the gcov file. Returns 390 NULL on failure (read past EOF). */ 391 392static const gcov_unsigned_t * 393gcov_read_words (unsigned words) 394{ 395 const gcov_unsigned_t *result; 396 unsigned excess = gcov_var.length - gcov_var.offset; 397 398 gcc_assert (gcov_var.mode > 0); 399 if (excess < words) 400 { 401 gcov_var.start += gcov_var.offset; 402#if IN_LIBGCOV 403 if (excess) 404 { 405 gcc_assert (excess == 1); 406 memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4); 407 } 408#else 409 memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4); 410#endif 411 gcov_var.offset = 0; 412 gcov_var.length = excess; 413#if IN_LIBGCOV 414 gcc_assert (!gcov_var.length || gcov_var.length == 1); 415 excess = GCOV_BLOCK_SIZE; 416#else 417 if (gcov_var.length + words > gcov_var.alloc) 418 gcov_allocate (gcov_var.length + words); 419 excess = gcov_var.alloc - gcov_var.length; 420#endif 421 excess = fread (gcov_var.buffer + gcov_var.length, 422 1, excess << 2, gcov_var.file) >> 2; 423 gcov_var.length += excess; 424 if (gcov_var.length < words) 425 { 426 gcov_var.overread += words - gcov_var.length; 427 gcov_var.length = 0; 428 return 0; 429 } 430 } 431 result = &gcov_var.buffer[gcov_var.offset]; 432 gcov_var.offset += words; 433 return result; 434} 435 436/* Read unsigned value from a coverage file. Sets error flag on file 437 error, overflow flag on overflow */ 438 439GCOV_LINKAGE gcov_unsigned_t 440gcov_read_unsigned (void) 441{ 442 gcov_unsigned_t value; 443 const gcov_unsigned_t *buffer = gcov_read_words (1); 444 445 if (!buffer) 446 return 0; 447 value = from_file (buffer[0]); 448 return value; 449} 450 451/* Read counter value from a coverage file. Sets error flag on file 452 error, overflow flag on overflow */ 453 454GCOV_LINKAGE gcov_type 455gcov_read_counter (void) 456{ 457 gcov_type value; 458 const gcov_unsigned_t *buffer = gcov_read_words (2); 459 460 if (!buffer) 461 return 0; 462 value = from_file (buffer[0]); 463 if (sizeof (value) > sizeof (gcov_unsigned_t)) 464 value |= ((gcov_type) from_file (buffer[1])) << 32; 465 else if (buffer[1]) 466 gcov_var.error = -1; 467 468 return value; 469} 470 471/* Read string from coverage file. Returns a pointer to a static 472 buffer, or NULL on empty string. You must copy the string before 473 calling another gcov function. */ 474 475#if !IN_LIBGCOV 476GCOV_LINKAGE const char * 477gcov_read_string (void) 478{ 479 unsigned length = gcov_read_unsigned (); 480 481 if (!length) 482 return 0; 483 484 return (const char *) gcov_read_words (length); 485} 486#endif 487 488GCOV_LINKAGE void 489gcov_read_summary (struct gcov_summary *summary) 490{ 491 unsigned ix; 492 struct gcov_ctr_summary *csum; 493 494 summary->checksum = gcov_read_unsigned (); 495 for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) 496 { 497 csum->num = gcov_read_unsigned (); 498 csum->runs = gcov_read_unsigned (); 499 csum->sum_all = gcov_read_counter (); 500 csum->run_max = gcov_read_counter (); 501 csum->sum_max = gcov_read_counter (); 502 } 503} 504 505#if !IN_LIBGCOV 506/* Reset to a known position. BASE should have been obtained from 507 gcov_position, LENGTH should be a record length. */ 508 509GCOV_LINKAGE void 510gcov_sync (gcov_position_t base, gcov_unsigned_t length) 511{ 512 gcc_assert (gcov_var.mode > 0); 513 base += length; 514 if (base - gcov_var.start <= gcov_var.length) 515 gcov_var.offset = base - gcov_var.start; 516 else 517 { 518 gcov_var.offset = gcov_var.length = 0; 519 fseek (gcov_var.file, base << 2, SEEK_SET); 520 gcov_var.start = ftell (gcov_var.file) >> 2; 521 } 522} 523#endif 524 525#if IN_LIBGCOV 526/* Move to a given position in a gcov file. */ 527 528GCOV_LINKAGE void 529gcov_seek (gcov_position_t base) 530{ 531 gcc_assert (gcov_var.mode < 0); 532 if (gcov_var.offset) 533 gcov_write_block (gcov_var.offset); 534 fseek (gcov_var.file, base << 2, SEEK_SET); 535 gcov_var.start = ftell (gcov_var.file) >> 2; 536} 537#endif 538 539#if IN_GCOV > 0 540/* Return the modification time of the current gcov file. */ 541 542GCOV_LINKAGE time_t 543gcov_time (void) 544{ 545 struct stat status; 546 547 if (fstat (fileno (gcov_var.file), &status)) 548 return 0; 549 else 550 return status.st_mtime; 551} 552#endif /* IN_GCOV */ 553