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