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 7 This file is part of GCC. 8 9 GCC is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free 11 Software Foundation; either version 3, or (at your option) any later 12 version. 13 14 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 for more details. 18 19 Under Section 7 of GPL version 3, you are granted additional 20 permissions described in the GCC Runtime Library Exception, version 21 3.1, as published by the Free Software Foundation. 22 23 You should have received a copy of the GNU General Public License and 24 a copy of the GCC Runtime Library Exception along with this program; 25 see 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 32 static void gcov_write_block (unsigned); 33 static gcov_unsigned_t *gcov_write_words (unsigned); 34 #endif 35 static const gcov_unsigned_t *gcov_read_words (unsigned); 36 #if !IN_LIBGCOV 37 static void gcov_allocate (unsigned); 38 #endif 39 40 static 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 62 GCOV_LINKAGE int 63 #if IN_LIBGCOV 64 gcov_open (const char *name) 65 #else 66 gcov_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 160 GCOV_LINKAGE int 161 gcov_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 187 GCOV_LINKAGE int 188 gcov_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 204 static void 205 gcov_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 222 static void 223 gcov_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 234 static gcov_unsigned_t * 235 gcov_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 263 GCOV_LINKAGE void 264 gcov_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 275 GCOV_LINKAGE void 276 gcov_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 292 GCOV_LINKAGE void 293 gcov_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 317 GCOV_LINKAGE gcov_position_t 318 gcov_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 334 GCOV_LINKAGE void 335 gcov_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 356 GCOV_LINKAGE void 357 gcov_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 368 GCOV_LINKAGE void 369 gcov_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 392 static const gcov_unsigned_t * 393 gcov_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 439 GCOV_LINKAGE gcov_unsigned_t 440 gcov_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 454 GCOV_LINKAGE gcov_type 455 gcov_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 476 GCOV_LINKAGE const char * 477 gcov_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 488 GCOV_LINKAGE void 489 gcov_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 509 GCOV_LINKAGE void 510 gcov_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 528 GCOV_LINKAGE void 529 gcov_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 542 GCOV_LINKAGE time_t 543 gcov_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