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