1132718Skan/* File format for coverage information 2169689Skan Copyright (C) 1996, 1997, 1998, 2000, 2002, 2003, 2004, 2005 3169689Skan Free Software Foundation, Inc. 4132718Skan Contributed by Bob Manson <manson@cygnus.com>. 5132718Skan Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. 6132718Skan 7132718SkanThis file is part of GCC. 8132718Skan 9132718SkanGCC is free software; you can redistribute it and/or modify it under 10132718Skanthe terms of the GNU General Public License as published by the Free 11132718SkanSoftware Foundation; either version 2, or (at your option) any later 12132718Skanversion. 13132718Skan 14132718SkanGCC is distributed in the hope that it will be useful, but WITHOUT ANY 15132718SkanWARRANTY; without even the implied warranty of MERCHANTABILITY or 16132718SkanFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17132718Skanfor more details. 18132718Skan 19132718SkanYou should have received a copy of the GNU General Public License 20132718Skanalong with GCC; see the file COPYING. If not, write to the Free 21169689SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 22169689Skan02110-1301, USA. */ 23132718Skan 24132718Skan/* Routines declared in gcov-io.h. This file should be #included by 25132718Skan another source file, after having #included gcov-io.h. */ 26132718Skan 27132718Skan#if !IN_GCOV 28132718Skanstatic void gcov_write_block (unsigned); 29132718Skanstatic gcov_unsigned_t *gcov_write_words (unsigned); 30132718Skan#endif 31132718Skanstatic const gcov_unsigned_t *gcov_read_words (unsigned); 32132718Skan#if !IN_LIBGCOV 33132718Skanstatic void gcov_allocate (unsigned); 34132718Skan#endif 35132718Skan 36132718Skanstatic inline gcov_unsigned_t from_file (gcov_unsigned_t value) 37132718Skan{ 38132718Skan#if !IN_LIBGCOV 39132718Skan if (gcov_var.endian) 40132718Skan { 41132718Skan value = (value >> 16) | (value << 16); 42132718Skan value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff); 43132718Skan } 44132718Skan#endif 45132718Skan return value; 46132718Skan} 47132718Skan 48132718Skan/* Open a gcov file. NAME is the name of the file to open and MODE 49132718Skan indicates whether a new file should be created, or an existing file 50132718Skan opened for modification. If MODE is >= 0 an existing file will be 51132718Skan opened, if possible, and if MODE is <= 0, a new file will be 52132718Skan created. Use MODE=0 to attempt to reopen an existing file and then 53132718Skan fall back on creating a new one. Return zero on failure, >0 on 54132718Skan opening an existing file and <0 on creating a new one. */ 55132718Skan 56132718SkanGCOV_LINKAGE int 57132718Skan#if IN_LIBGCOV 58132718Skangcov_open (const char *name) 59132718Skan#else 60132718Skangcov_open (const char *name, int mode) 61132718Skan#endif 62132718Skan{ 63132718Skan#if IN_LIBGCOV 64132718Skan const int mode = 0; 65132718Skan#endif 66132718Skan#if GCOV_LOCKED 67132718Skan struct flock s_flock; 68132718Skan int fd; 69132718Skan 70132718Skan s_flock.l_type = F_WRLCK; 71132718Skan s_flock.l_whence = SEEK_SET; 72132718Skan s_flock.l_start = 0; 73132718Skan s_flock.l_len = 0; /* Until EOF. */ 74132718Skan s_flock.l_pid = getpid (); 75132718Skan#endif 76132718Skan 77169689Skan gcc_assert (!gcov_var.file); 78132718Skan gcov_var.start = 0; 79132718Skan gcov_var.offset = gcov_var.length = 0; 80132718Skan gcov_var.overread = -1u; 81132718Skan gcov_var.error = 0; 82132718Skan#if !IN_LIBGCOV 83132718Skan gcov_var.endian = 0; 84132718Skan#endif 85132718Skan#if GCOV_LOCKED 86132718Skan if (mode > 0) 87132718Skan fd = open (name, O_RDWR); 88132718Skan else 89132718Skan fd = open (name, O_RDWR | O_CREAT, 0666); 90132718Skan if (fd < 0) 91132718Skan return 0; 92132718Skan 93132718Skan while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR) 94132718Skan continue; 95132718Skan 96132718Skan gcov_var.file = fdopen (fd, "r+b"); 97132718Skan if (!gcov_var.file) 98132718Skan { 99132718Skan close (fd); 100132718Skan return 0; 101132718Skan } 102132718Skan 103132718Skan if (mode > 0) 104132718Skan gcov_var.mode = 1; 105132718Skan else if (mode == 0) 106132718Skan { 107132718Skan struct stat st; 108132718Skan 109132718Skan if (fstat (fd, &st) < 0) 110132718Skan { 111132718Skan fclose (gcov_var.file); 112132718Skan gcov_var.file = 0; 113132718Skan return 0; 114132718Skan } 115132718Skan if (st.st_size != 0) 116132718Skan gcov_var.mode = 1; 117132718Skan else 118132718Skan gcov_var.mode = mode * 2 + 1; 119132718Skan } 120132718Skan else 121132718Skan gcov_var.mode = mode * 2 + 1; 122132718Skan#else 123132718Skan if (mode >= 0) 124132718Skan gcov_var.file = fopen (name, "r+b"); 125132718Skan if (gcov_var.file) 126132718Skan gcov_var.mode = 1; 127132718Skan else if (mode <= 0) 128132718Skan { 129132718Skan gcov_var.file = fopen (name, "w+b"); 130132718Skan if (gcov_var.file) 131132718Skan gcov_var.mode = mode * 2 + 1; 132132718Skan } 133132718Skan if (!gcov_var.file) 134132718Skan return 0; 135132718Skan#endif 136132718Skan 137132718Skan setbuf (gcov_var.file, (char *)0); 138132718Skan 139132718Skan return 1; 140132718Skan} 141132718Skan 142132718Skan/* Close the current gcov file. Flushes data to disk. Returns nonzero 143132718Skan on failure or error flag set. */ 144132718Skan 145132718SkanGCOV_LINKAGE int 146132718Skangcov_close (void) 147132718Skan{ 148132718Skan if (gcov_var.file) 149132718Skan { 150132718Skan#if !IN_GCOV 151132718Skan if (gcov_var.offset && gcov_var.mode < 0) 152132718Skan gcov_write_block (gcov_var.offset); 153132718Skan#endif 154132718Skan fclose (gcov_var.file); 155132718Skan gcov_var.file = 0; 156132718Skan gcov_var.length = 0; 157132718Skan } 158132718Skan#if !IN_LIBGCOV 159132718Skan free (gcov_var.buffer); 160132718Skan gcov_var.alloc = 0; 161132718Skan gcov_var.buffer = 0; 162132718Skan#endif 163132718Skan gcov_var.mode = 0; 164132718Skan return gcov_var.error; 165132718Skan} 166132718Skan 167132718Skan#if !IN_LIBGCOV 168132718Skan/* Check if MAGIC is EXPECTED. Use it to determine endianness of the 169132718Skan file. Returns +1 for same endian, -1 for other endian and zero for 170132718Skan not EXPECTED. */ 171132718Skan 172132718SkanGCOV_LINKAGE int 173132718Skangcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected) 174132718Skan{ 175132718Skan if (magic == expected) 176132718Skan return 1; 177132718Skan magic = (magic >> 16) | (magic << 16); 178132718Skan magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff); 179132718Skan if (magic == expected) 180132718Skan { 181132718Skan gcov_var.endian = 1; 182132718Skan return -1; 183132718Skan } 184132718Skan return 0; 185132718Skan} 186132718Skan#endif 187132718Skan 188132718Skan#if !IN_LIBGCOV 189132718Skanstatic void 190132718Skangcov_allocate (unsigned length) 191132718Skan{ 192132718Skan size_t new_size = gcov_var.alloc; 193132718Skan 194132718Skan if (!new_size) 195132718Skan new_size = GCOV_BLOCK_SIZE; 196132718Skan new_size += length; 197132718Skan new_size *= 2; 198132718Skan 199132718Skan gcov_var.alloc = new_size; 200132718Skan gcov_var.buffer = xrealloc (gcov_var.buffer, new_size << 2); 201132718Skan} 202132718Skan#endif 203132718Skan 204132718Skan#if !IN_GCOV 205132718Skan/* Write out the current block, if needs be. */ 206132718Skan 207132718Skanstatic void 208132718Skangcov_write_block (unsigned size) 209132718Skan{ 210132718Skan if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1) 211132718Skan gcov_var.error = 1; 212132718Skan gcov_var.start += size; 213132718Skan gcov_var.offset -= size; 214132718Skan} 215132718Skan 216132718Skan/* Allocate space to write BYTES bytes to the gcov file. Return a 217132718Skan pointer to those bytes, or NULL on failure. */ 218132718Skan 219132718Skanstatic gcov_unsigned_t * 220132718Skangcov_write_words (unsigned words) 221132718Skan{ 222132718Skan gcov_unsigned_t *result; 223132718Skan 224169689Skan gcc_assert (gcov_var.mode < 0); 225132718Skan#if IN_LIBGCOV 226132718Skan if (gcov_var.offset >= GCOV_BLOCK_SIZE) 227132718Skan { 228132718Skan gcov_write_block (GCOV_BLOCK_SIZE); 229132718Skan if (gcov_var.offset) 230132718Skan { 231169689Skan gcc_assert (gcov_var.offset == 1); 232132718Skan memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4); 233132718Skan } 234132718Skan } 235132718Skan#else 236132718Skan if (gcov_var.offset + words > gcov_var.alloc) 237132718Skan gcov_allocate (gcov_var.offset + words); 238132718Skan#endif 239132718Skan result = &gcov_var.buffer[gcov_var.offset]; 240132718Skan gcov_var.offset += words; 241132718Skan 242132718Skan return result; 243132718Skan} 244132718Skan 245132718Skan/* Write unsigned VALUE to coverage file. Sets error flag 246132718Skan appropriately. */ 247132718Skan 248132718SkanGCOV_LINKAGE void 249132718Skangcov_write_unsigned (gcov_unsigned_t value) 250132718Skan{ 251132718Skan gcov_unsigned_t *buffer = gcov_write_words (1); 252132718Skan 253132718Skan buffer[0] = value; 254132718Skan} 255132718Skan 256132718Skan/* Write counter VALUE to coverage file. Sets error flag 257132718Skan appropriately. */ 258132718Skan 259132718Skan#if IN_LIBGCOV 260132718SkanGCOV_LINKAGE void 261132718Skangcov_write_counter (gcov_type value) 262132718Skan{ 263132718Skan gcov_unsigned_t *buffer = gcov_write_words (2); 264132718Skan 265132718Skan buffer[0] = (gcov_unsigned_t) value; 266132718Skan if (sizeof (value) > sizeof (gcov_unsigned_t)) 267132718Skan buffer[1] = (gcov_unsigned_t) (value >> 32); 268132718Skan else 269132718Skan buffer[1] = 0; 270132718Skan} 271132718Skan#endif /* IN_LIBGCOV */ 272132718Skan 273132718Skan#if !IN_LIBGCOV 274132718Skan/* Write STRING to coverage file. Sets error flag on file 275132718Skan error, overflow flag on overflow */ 276132718Skan 277132718SkanGCOV_LINKAGE void 278132718Skangcov_write_string (const char *string) 279132718Skan{ 280132718Skan unsigned length = 0; 281132718Skan unsigned alloc = 0; 282132718Skan gcov_unsigned_t *buffer; 283132718Skan 284132718Skan if (string) 285132718Skan { 286132718Skan length = strlen (string); 287132718Skan alloc = (length + 4) >> 2; 288132718Skan } 289132718Skan 290132718Skan buffer = gcov_write_words (1 + alloc); 291132718Skan 292132718Skan buffer[0] = alloc; 293132718Skan buffer[alloc] = 0; 294132718Skan memcpy (&buffer[1], string, length); 295132718Skan} 296132718Skan#endif 297132718Skan 298132718Skan#if !IN_LIBGCOV 299132718Skan/* Write a tag TAG and reserve space for the record length. Return a 300132718Skan value to be used for gcov_write_length. */ 301132718Skan 302132718SkanGCOV_LINKAGE gcov_position_t 303132718Skangcov_write_tag (gcov_unsigned_t tag) 304132718Skan{ 305132718Skan gcov_position_t result = gcov_var.start + gcov_var.offset; 306132718Skan gcov_unsigned_t *buffer = gcov_write_words (2); 307132718Skan 308132718Skan buffer[0] = tag; 309132718Skan buffer[1] = 0; 310132718Skan 311132718Skan return result; 312132718Skan} 313132718Skan 314132718Skan/* Write a record length using POSITION, which was returned by 315132718Skan gcov_write_tag. The current file position is the end of the 316132718Skan record, and is restored before returning. Returns nonzero on 317132718Skan overflow. */ 318132718Skan 319132718SkanGCOV_LINKAGE void 320132718Skangcov_write_length (gcov_position_t position) 321132718Skan{ 322132718Skan unsigned offset; 323132718Skan gcov_unsigned_t length; 324132718Skan gcov_unsigned_t *buffer; 325132718Skan 326169689Skan gcc_assert (gcov_var.mode < 0); 327169689Skan gcc_assert (position + 2 <= gcov_var.start + gcov_var.offset); 328169689Skan gcc_assert (position >= gcov_var.start); 329132718Skan offset = position - gcov_var.start; 330132718Skan length = gcov_var.offset - offset - 2; 331132718Skan buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset]; 332132718Skan buffer[1] = length; 333132718Skan if (gcov_var.offset >= GCOV_BLOCK_SIZE) 334132718Skan gcov_write_block (gcov_var.offset); 335132718Skan} 336132718Skan 337132718Skan#else /* IN_LIBGCOV */ 338132718Skan 339132718Skan/* Write a tag TAG and length LENGTH. */ 340132718Skan 341132718SkanGCOV_LINKAGE void 342132718Skangcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length) 343132718Skan{ 344132718Skan gcov_unsigned_t *buffer = gcov_write_words (2); 345132718Skan 346132718Skan buffer[0] = tag; 347132718Skan buffer[1] = length; 348132718Skan} 349132718Skan 350132718Skan/* Write a summary structure to the gcov file. Return nonzero on 351132718Skan overflow. */ 352132718Skan 353132718SkanGCOV_LINKAGE void 354132718Skangcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) 355132718Skan{ 356132718Skan unsigned ix; 357132718Skan const struct gcov_ctr_summary *csum; 358132718Skan 359132718Skan gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH); 360132718Skan gcov_write_unsigned (summary->checksum); 361132718Skan for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) 362132718Skan { 363132718Skan gcov_write_unsigned (csum->num); 364132718Skan gcov_write_unsigned (csum->runs); 365132718Skan gcov_write_counter (csum->sum_all); 366132718Skan gcov_write_counter (csum->run_max); 367132718Skan gcov_write_counter (csum->sum_max); 368132718Skan } 369132718Skan} 370132718Skan#endif /* IN_LIBGCOV */ 371132718Skan 372132718Skan#endif /*!IN_GCOV */ 373132718Skan 374132718Skan/* Return a pointer to read BYTES bytes from the gcov file. Returns 375132718Skan NULL on failure (read past EOF). */ 376132718Skan 377132718Skanstatic const gcov_unsigned_t * 378132718Skangcov_read_words (unsigned words) 379132718Skan{ 380132718Skan const gcov_unsigned_t *result; 381132718Skan unsigned excess = gcov_var.length - gcov_var.offset; 382132718Skan 383169689Skan gcc_assert (gcov_var.mode > 0); 384132718Skan if (excess < words) 385132718Skan { 386132718Skan gcov_var.start += gcov_var.offset; 387132718Skan#if IN_LIBGCOV 388132718Skan if (excess) 389132718Skan { 390169689Skan gcc_assert (excess == 1); 391132718Skan memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4); 392132718Skan } 393132718Skan#else 394132718Skan memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4); 395132718Skan#endif 396132718Skan gcov_var.offset = 0; 397132718Skan gcov_var.length = excess; 398132718Skan#if IN_LIBGCOV 399169689Skan gcc_assert (!gcov_var.length || gcov_var.length == 1); 400132718Skan excess = GCOV_BLOCK_SIZE; 401132718Skan#else 402132718Skan if (gcov_var.length + words > gcov_var.alloc) 403132718Skan gcov_allocate (gcov_var.length + words); 404132718Skan excess = gcov_var.alloc - gcov_var.length; 405132718Skan#endif 406132718Skan excess = fread (gcov_var.buffer + gcov_var.length, 407132718Skan 1, excess << 2, gcov_var.file) >> 2; 408132718Skan gcov_var.length += excess; 409132718Skan if (gcov_var.length < words) 410132718Skan { 411132718Skan gcov_var.overread += words - gcov_var.length; 412132718Skan gcov_var.length = 0; 413132718Skan return 0; 414132718Skan } 415132718Skan } 416132718Skan result = &gcov_var.buffer[gcov_var.offset]; 417132718Skan gcov_var.offset += words; 418132718Skan return result; 419132718Skan} 420132718Skan 421132718Skan/* Read unsigned value from a coverage file. Sets error flag on file 422132718Skan error, overflow flag on overflow */ 423132718Skan 424132718SkanGCOV_LINKAGE gcov_unsigned_t 425132718Skangcov_read_unsigned (void) 426132718Skan{ 427132718Skan gcov_unsigned_t value; 428132718Skan const gcov_unsigned_t *buffer = gcov_read_words (1); 429132718Skan 430132718Skan if (!buffer) 431132718Skan return 0; 432132718Skan value = from_file (buffer[0]); 433132718Skan return value; 434132718Skan} 435132718Skan 436132718Skan/* Read counter value from a coverage file. Sets error flag on file 437132718Skan error, overflow flag on overflow */ 438132718Skan 439132718SkanGCOV_LINKAGE gcov_type 440132718Skangcov_read_counter (void) 441132718Skan{ 442132718Skan gcov_type value; 443132718Skan const gcov_unsigned_t *buffer = gcov_read_words (2); 444132718Skan 445132718Skan if (!buffer) 446132718Skan return 0; 447132718Skan value = from_file (buffer[0]); 448132718Skan if (sizeof (value) > sizeof (gcov_unsigned_t)) 449132718Skan value |= ((gcov_type) from_file (buffer[1])) << 32; 450132718Skan else if (buffer[1]) 451132718Skan gcov_var.error = -1; 452169689Skan 453132718Skan return value; 454132718Skan} 455132718Skan 456132718Skan/* Read string from coverage file. Returns a pointer to a static 457132718Skan buffer, or NULL on empty string. You must copy the string before 458132718Skan calling another gcov function. */ 459132718Skan 460132718Skan#if !IN_LIBGCOV 461132718SkanGCOV_LINKAGE const char * 462132718Skangcov_read_string (void) 463132718Skan{ 464132718Skan unsigned length = gcov_read_unsigned (); 465132718Skan 466132718Skan if (!length) 467132718Skan return 0; 468132718Skan 469132718Skan return (const char *) gcov_read_words (length); 470132718Skan} 471132718Skan#endif 472132718Skan 473132718SkanGCOV_LINKAGE void 474132718Skangcov_read_summary (struct gcov_summary *summary) 475132718Skan{ 476132718Skan unsigned ix; 477132718Skan struct gcov_ctr_summary *csum; 478132718Skan 479132718Skan summary->checksum = gcov_read_unsigned (); 480132718Skan for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) 481132718Skan { 482132718Skan csum->num = gcov_read_unsigned (); 483132718Skan csum->runs = gcov_read_unsigned (); 484132718Skan csum->sum_all = gcov_read_counter (); 485132718Skan csum->run_max = gcov_read_counter (); 486132718Skan csum->sum_max = gcov_read_counter (); 487132718Skan } 488132718Skan} 489132718Skan 490132718Skan#if !IN_LIBGCOV 491132718Skan/* Reset to a known position. BASE should have been obtained from 492132718Skan gcov_position, LENGTH should be a record length. */ 493132718Skan 494132718SkanGCOV_LINKAGE void 495132718Skangcov_sync (gcov_position_t base, gcov_unsigned_t length) 496132718Skan{ 497169689Skan gcc_assert (gcov_var.mode > 0); 498132718Skan base += length; 499132718Skan if (base - gcov_var.start <= gcov_var.length) 500132718Skan gcov_var.offset = base - gcov_var.start; 501132718Skan else 502132718Skan { 503132718Skan gcov_var.offset = gcov_var.length = 0; 504132718Skan fseek (gcov_var.file, base << 2, SEEK_SET); 505132718Skan gcov_var.start = ftell (gcov_var.file) >> 2; 506132718Skan } 507132718Skan} 508132718Skan#endif 509132718Skan 510132718Skan#if IN_LIBGCOV 511169689Skan/* Move to the a set position in a gcov file. */ 512132718Skan 513132718SkanGCOV_LINKAGE void 514132718Skangcov_seek (gcov_position_t base) 515132718Skan{ 516169689Skan gcc_assert (gcov_var.mode < 0); 517132718Skan if (gcov_var.offset) 518132718Skan gcov_write_block (gcov_var.offset); 519169689Skan fseek (gcov_var.file, base << 2, SEEK_SET); 520132718Skan gcov_var.start = ftell (gcov_var.file) >> 2; 521132718Skan} 522132718Skan#endif 523132718Skan 524132718Skan#if IN_GCOV > 0 525132718Skan/* Return the modification time of the current gcov file. */ 526132718Skan 527132718SkanGCOV_LINKAGE time_t 528132718Skangcov_time (void) 529132718Skan{ 530132718Skan struct stat status; 531132718Skan 532132718Skan if (fstat (fileno (gcov_var.file), &status)) 533132718Skan return 0; 534132718Skan else 535132718Skan return status.st_mtime; 536132718Skan} 537132718Skan#endif /* IN_GCOV */ 538