]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/gcov-io.c
Update copyright years.
[thirdparty/gcc.git] / gcc / gcov-io.c
CommitLineData
44359ced 1/* File format for coverage information
fbd26352 2 Copyright (C) 1996-2019 Free Software Foundation, Inc.
44359ced 3 Contributed by Bob Manson <manson@cygnus.com>.
4 Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
5
6This file is part of GCC.
7
8GCC is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
8c4c00c1 10Software Foundation; either version 3, or (at your option) any later
44359ced 11version.
12
13GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
cf09cacb 18Under Section 7 of GPL version 3, you are granted additional
19permissions described in the GCC Runtime Library Exception, version
203.1, as published by the Free Software Foundation.
21
22You should have received a copy of the GNU General Public License and
23a copy of the GCC Runtime Library Exception along with this program;
24see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
8c4c00c1 25<http://www.gnu.org/licenses/>. */
44359ced 26
27/* Routines declared in gcov-io.h. This file should be #included by
28 another source file, after having #included gcov-io.h. */
29
80abd9e4 30#if !IN_GCOV
31static void gcov_write_block (unsigned);
9e0943d4 32static gcov_unsigned_t *gcov_write_words (unsigned);
80abd9e4 33#endif
9e0943d4 34static const gcov_unsigned_t *gcov_read_words (unsigned);
80abd9e4 35#if !IN_LIBGCOV
36static void gcov_allocate (unsigned);
37#endif
38
67745126 39/* Optimum number of gcov_unsigned_t's read from or written to disk. */
40#define GCOV_BLOCK_SIZE (1 << 10)
41
f6c27254 42struct gcov_var
67745126 43{
44 FILE *file;
45 gcov_position_t start; /* Position of first byte of block */
46 unsigned offset; /* Read/write position within the block. */
47 unsigned length; /* Read limit in the block. */
48 unsigned overread; /* Number of words overread. */
49 int error; /* < 0 overflow, > 0 disk error. */
50 int mode; /* < 0 writing, > 0 reading */
51#if IN_LIBGCOV
52 /* Holds one block plus 4 bytes, thus all coverage reads & writes
53 fit within this buffer and we always can transfer GCOV_BLOCK_SIZE
54 to and from the disk. libgcov never backtracks and only writes 4
55 or 8 byte objects. */
56 gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1];
57#else
58 int endian; /* Swap endianness. */
59 /* Holds a variable length block, as the compiler can write
60 strings and needs to backtrack. */
61 size_t alloc;
62 gcov_unsigned_t *buffer;
63#endif
64} gcov_var;
65
66/* Save the current position in the gcov file. */
e76f6040 67/* We need to expose this function when compiling for gcov-tool. */
68#ifndef IN_GCOV_TOOL
69static inline
70#endif
71gcov_position_t
67745126 72gcov_position (void)
73{
f1919901 74 gcov_nonruntime_assert (gcov_var.mode > 0);
67745126 75 return gcov_var.start + gcov_var.offset;
76}
77
78/* Return nonzero if the error flag is set. */
e76f6040 79/* We need to expose this function when compiling for gcov-tool. */
80#ifndef IN_GCOV_TOOL
81static inline
82#endif
83int
67745126 84gcov_is_error (void)
85{
86 return gcov_var.file ? gcov_var.error : 1;
87}
88
89#if IN_LIBGCOV
90/* Move to beginning of file and initialize for writing. */
91GCOV_LINKAGE inline void
92gcov_rewrite (void)
93{
67745126 94 gcov_var.mode = -1;
95 gcov_var.start = 0;
96 gcov_var.offset = 0;
97 fseek (gcov_var.file, 0L, SEEK_SET);
98}
99#endif
100
b4d48d67 101static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
102{
103#if !IN_LIBGCOV
104 if (gcov_var.endian)
105 {
106 value = (value >> 16) | (value << 16);
107 value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
108 }
109#endif
110 return value;
111}
112
44359ced 113/* Open a gcov file. NAME is the name of the file to open and MODE
114 indicates whether a new file should be created, or an existing file
35ab2622 115 opened. If MODE is >= 0 an existing file will be opened, if
116 possible, and if MODE is <= 0, a new file will be created. Use
117 MODE=0 to attempt to reopen an existing file and then fall back on
3623b4df 118 creating a new one. If MODE > 0, the file will be opened in
35ab2622 119 read-only mode. Otherwise it will be opened for modification.
3623b4df 120 Return zero on failure, non-zero on success. */
44359ced 121
122GCOV_LINKAGE int
b4d48d67 123#if IN_LIBGCOV
124gcov_open (const char *name)
125#else
44359ced 126gcov_open (const char *name, int mode)
b4d48d67 127#endif
44359ced 128{
b4d48d67 129#if IN_LIBGCOV
c3ffd8ac 130 int mode = 0;
b4d48d67 131#endif
3ddf7676 132#if GCOV_LOCKED
44359ced 133 struct flock s_flock;
9131ff41 134 int fd;
44359ced 135
44359ced 136 s_flock.l_whence = SEEK_SET;
137 s_flock.l_start = 0;
138 s_flock.l_len = 0; /* Until EOF. */
139 s_flock.l_pid = getpid ();
140#endif
48e1416a 141
f1919901 142 gcov_nonruntime_assert (!gcov_var.file);
80abd9e4 143 gcov_var.start = 0;
144 gcov_var.offset = gcov_var.length = 0;
9e0943d4 145 gcov_var.overread = -1u;
80abd9e4 146 gcov_var.error = 0;
b4d48d67 147#if !IN_LIBGCOV
148 gcov_var.endian = 0;
149#endif
9131ff41 150#if GCOV_LOCKED
151 if (mode > 0)
35ab2622 152 {
153 /* Read-only mode - acquire a read-lock. */
154 s_flock.l_type = F_RDLCK;
d3cf0a31 155 /* pass mode (ignored) for compatibility */
156 fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR);
35ab2622 157 }
3623b4df 158 else
ca3f7d34 159 {
160 /* Write mode - acquire a write-lock. */
161 s_flock.l_type = F_WRLCK;
3623b4df 162 /* Truncate if force new mode. */
163 fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666);
35ab2622 164 }
9131ff41 165 if (fd < 0)
166 return 0;
167
168 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
169 continue;
170
35ab2622 171 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
172
9131ff41 173 if (!gcov_var.file)
174 {
175 close (fd);
176 return 0;
177 }
9131ff41 178#else
44359ced 179 if (mode >= 0)
3623b4df 180 /* Open an existing file. */
35ab2622 181 gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
182
80abd9e4 183 if (gcov_var.file)
3623b4df 184 mode = 1;
80abd9e4 185 else if (mode <= 0)
3623b4df 186 /* Create a new file. */
187 gcov_var.file = fopen (name, "w+b");
188
44359ced 189 if (!gcov_var.file)
190 return 0;
44359ced 191#endif
192
3623b4df 193 gcov_var.mode = mode ? mode : 1;
194
9131ff41 195 setbuf (gcov_var.file, (char *)0);
48e1416a 196
b4d48d67 197 return 1;
44359ced 198}
199
200/* Close the current gcov file. Flushes data to disk. Returns nonzero
201 on failure or error flag set. */
202
203GCOV_LINKAGE int
53b2c31f 204gcov_close (void)
44359ced 205{
44359ced 206 if (gcov_var.file)
207 {
80abd9e4 208#if !IN_GCOV
209 if (gcov_var.offset && gcov_var.mode < 0)
210 gcov_write_block (gcov_var.offset);
211#endif
44359ced 212 fclose (gcov_var.file);
213 gcov_var.file = 0;
214 gcov_var.length = 0;
215 }
216#if !IN_LIBGCOV
217 free (gcov_var.buffer);
218 gcov_var.alloc = 0;
219 gcov_var.buffer = 0;
220#endif
80abd9e4 221 gcov_var.mode = 0;
222 return gcov_var.error;
223}
224
b4d48d67 225#if !IN_LIBGCOV
226/* Check if MAGIC is EXPECTED. Use it to determine endianness of the
227 file. Returns +1 for same endian, -1 for other endian and zero for
228 not EXPECTED. */
229
230GCOV_LINKAGE int
231gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
232{
233 if (magic == expected)
234 return 1;
235 magic = (magic >> 16) | (magic << 16);
236 magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
237 if (magic == expected)
238 {
239 gcov_var.endian = 1;
240 return -1;
241 }
242 return 0;
243}
244#endif
245
80abd9e4 246#if !IN_LIBGCOV
247static void
248gcov_allocate (unsigned length)
249{
250 size_t new_size = gcov_var.alloc;
48e1416a 251
80abd9e4 252 if (!new_size)
253 new_size = GCOV_BLOCK_SIZE;
254 new_size += length;
255 new_size *= 2;
48e1416a 256
80abd9e4 257 gcov_var.alloc = new_size;
2457c754 258 gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
44359ced 259}
80abd9e4 260#endif
44359ced 261
262#if !IN_GCOV
80abd9e4 263/* Write out the current block, if needs be. */
264
265static void
266gcov_write_block (unsigned size)
267{
9e0943d4 268 if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
80abd9e4 269 gcov_var.error = 1;
270 gcov_var.start += size;
271 gcov_var.offset -= size;
272}
273
44359ced 274/* Allocate space to write BYTES bytes to the gcov file. Return a
275 pointer to those bytes, or NULL on failure. */
276
b4d48d67 277static gcov_unsigned_t *
9e0943d4 278gcov_write_words (unsigned words)
44359ced 279{
b4d48d67 280 gcov_unsigned_t *result;
44359ced 281
f1919901 282 gcov_nonruntime_assert (gcov_var.mode < 0);
44359ced 283#if IN_LIBGCOV
80abd9e4 284 if (gcov_var.offset >= GCOV_BLOCK_SIZE)
285 {
286 gcov_write_block (GCOV_BLOCK_SIZE);
287 if (gcov_var.offset)
44359ced 288 {
80abd9e4 289 memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
44359ced 290 }
80abd9e4 291 }
44359ced 292#else
9e0943d4 293 if (gcov_var.offset + words > gcov_var.alloc)
294 gcov_allocate (gcov_var.offset + words);
44359ced 295#endif
9e0943d4 296 result = &gcov_var.buffer[gcov_var.offset];
297 gcov_var.offset += words;
48e1416a 298
44359ced 299 return result;
300}
301
302/* Write unsigned VALUE to coverage file. Sets error flag
303 appropriately. */
304
305GCOV_LINKAGE void
834f169c 306gcov_write_unsigned (gcov_unsigned_t value)
44359ced 307{
9e0943d4 308 gcov_unsigned_t *buffer = gcov_write_words (1);
44359ced 309
b4d48d67 310 buffer[0] = value;
44359ced 311}
312
313/* Write counter VALUE to coverage file. Sets error flag
314 appropriately. */
315
316#if IN_LIBGCOV
317GCOV_LINKAGE void
318gcov_write_counter (gcov_type value)
319{
9e0943d4 320 gcov_unsigned_t *buffer = gcov_write_words (2);
44359ced 321
b4d48d67 322 buffer[0] = (gcov_unsigned_t) value;
323 if (sizeof (value) > sizeof (gcov_unsigned_t))
324 buffer[1] = (gcov_unsigned_t) (value >> 32);
325 else
326 buffer[1] = 0;
44359ced 327}
328#endif /* IN_LIBGCOV */
329
3f2c2dd8 330#if !IN_LIBGCOV
44359ced 331/* Write STRING to coverage file. Sets error flag on file
332 error, overflow flag on overflow */
333
334GCOV_LINKAGE void
335gcov_write_string (const char *string)
336{
337 unsigned length = 0;
b4d48d67 338 unsigned alloc = 0;
339 gcov_unsigned_t *buffer;
44359ced 340
341 if (string)
342 {
343 length = strlen (string);
b4d48d67 344 alloc = (length + 4) >> 2;
44359ced 345 }
48e1416a 346
9e0943d4 347 buffer = gcov_write_words (1 + alloc);
80abd9e4 348
b4d48d67 349 buffer[0] = alloc;
c8572ff7 350
351 if (alloc > 0)
352 {
353 buffer[alloc] = 0; /* place nul terminators. */
354 memcpy (&buffer[1], string, length);
355 }
44359ced 356}
3f2c2dd8 357#endif
44359ced 358
9bdf2425 359#if !IN_LIBGCOV
360/* Write FILENAME to coverage file. Sets error flag on file
361 error, overflow flag on overflow */
362
363GCOV_LINKAGE void
364gcov_write_filename (const char *filename)
365{
366 if (profile_abs_path_flag && filename && filename[0]
367 && !(IS_DIR_SEPARATOR (filename[0])
368#if HAVE_DOS_BASED_FILE_SYSTEM
369 || filename[1] == ':'
370#endif
371 ))
372 {
373 char *buf = getcwd (NULL, 0);
374 if (buf != NULL && buf[0])
375 {
376 size_t len = strlen (buf);
377 buf = (char*)xrealloc (buf, len + strlen (filename) + 2);
378 if (!IS_DIR_SEPARATOR (buf[len - 1]))
379 strcat (buf, "/");
380 strcat (buf, filename);
381 gcov_write_string (buf);
382 free (buf);
383 return;
384 }
385 }
386
387 gcov_write_string (filename);
388}
389#endif
390
3ddf7676 391#if !IN_LIBGCOV
44359ced 392/* Write a tag TAG and reserve space for the record length. Return a
393 value to be used for gcov_write_length. */
394
834f169c 395GCOV_LINKAGE gcov_position_t
396gcov_write_tag (gcov_unsigned_t tag)
44359ced 397{
80abd9e4 398 gcov_position_t result = gcov_var.start + gcov_var.offset;
9e0943d4 399 gcov_unsigned_t *buffer = gcov_write_words (2);
44359ced 400
b4d48d67 401 buffer[0] = tag;
402 buffer[1] = 0;
48e1416a 403
44359ced 404 return result;
405}
406
407/* Write a record length using POSITION, which was returned by
408 gcov_write_tag. The current file position is the end of the
409 record, and is restored before returning. Returns nonzero on
410 overflow. */
411
412GCOV_LINKAGE void
834f169c 413gcov_write_length (gcov_position_t position)
44359ced 414{
80abd9e4 415 unsigned offset;
416 gcov_unsigned_t length;
b4d48d67 417 gcov_unsigned_t *buffer;
80abd9e4 418
f1919901 419 gcov_nonruntime_assert (gcov_var.mode < 0);
420 gcov_nonruntime_assert (position + 2 <= gcov_var.start + gcov_var.offset);
421 gcov_nonruntime_assert (position >= gcov_var.start);
80abd9e4 422 offset = position - gcov_var.start;
9e0943d4 423 length = gcov_var.offset - offset - 2;
b4d48d67 424 buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset];
425 buffer[1] = length;
80abd9e4 426 if (gcov_var.offset >= GCOV_BLOCK_SIZE)
427 gcov_write_block (gcov_var.offset);
44359ced 428}
834f169c 429
430#else /* IN_LIBGCOV */
3ddf7676 431
432/* Write a tag TAG and length LENGTH. */
433
434GCOV_LINKAGE void
834f169c 435gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
3ddf7676 436{
9e0943d4 437 gcov_unsigned_t *buffer = gcov_write_words (2);
3ddf7676 438
b4d48d67 439 buffer[0] = tag;
440 buffer[1] = length;
3ddf7676 441}
44359ced 442
7ef5b942 443/* Write a summary structure to the gcov file. Return nonzero on
44359ced 444 overflow. */
445
446GCOV_LINKAGE void
834f169c 447gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
44359ced 448{
56621355 449 gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
5860b185 450 gcov_write_unsigned (summary->runs);
56621355 451 gcov_write_unsigned (summary->sum_max);
44359ced 452}
56621355 453
44359ced 454#endif /* IN_LIBGCOV */
455
456#endif /*!IN_GCOV */
457
458/* Return a pointer to read BYTES bytes from the gcov file. Returns
6473f3f4 459 NULL on failure (read past EOF). */
44359ced 460
b4d48d67 461static const gcov_unsigned_t *
9e0943d4 462gcov_read_words (unsigned words)
44359ced 463{
b4d48d67 464 const gcov_unsigned_t *result;
80abd9e4 465 unsigned excess = gcov_var.length - gcov_var.offset;
48e1416a 466
b1d71150 467 if (gcov_var.mode <= 0)
468 return NULL;
469
9e0943d4 470 if (excess < words)
44359ced 471 {
80abd9e4 472 gcov_var.start += gcov_var.offset;
80abd9e4 473 if (excess)
474 {
d69521d8 475#if IN_LIBGCOV
80abd9e4 476 memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
80abd9e4 477#else
d69521d8 478 memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset,
479 excess * 4);
80abd9e4 480#endif
d69521d8 481 }
80abd9e4 482 gcov_var.offset = 0;
483 gcov_var.length = excess;
484#if IN_LIBGCOV
80abd9e4 485 excess = GCOV_BLOCK_SIZE;
486#else
9e0943d4 487 if (gcov_var.length + words > gcov_var.alloc)
488 gcov_allocate (gcov_var.length + words);
80abd9e4 489 excess = gcov_var.alloc - gcov_var.length;
490#endif
1278fdc3 491 excess = fread (gcov_var.buffer + gcov_var.length,
9e0943d4 492 1, excess << 2, gcov_var.file) >> 2;
80abd9e4 493 gcov_var.length += excess;
9e0943d4 494 if (gcov_var.length < words)
80abd9e4 495 {
9e0943d4 496 gcov_var.overread += words - gcov_var.length;
80abd9e4 497 gcov_var.length = 0;
498 return 0;
499 }
44359ced 500 }
9e0943d4 501 result = &gcov_var.buffer[gcov_var.offset];
502 gcov_var.offset += words;
44359ced 503 return result;
504}
505
506/* Read unsigned value from a coverage file. Sets error flag on file
507 error, overflow flag on overflow */
508
834f169c 509GCOV_LINKAGE gcov_unsigned_t
53b2c31f 510gcov_read_unsigned (void)
44359ced 511{
b4d48d67 512 gcov_unsigned_t value;
9e0943d4 513 const gcov_unsigned_t *buffer = gcov_read_words (1);
44359ced 514
515 if (!buffer)
516 return 0;
b4d48d67 517 value = from_file (buffer[0]);
44359ced 518 return value;
519}
520
521/* Read counter value from a coverage file. Sets error flag on file
522 error, overflow flag on overflow */
523
524GCOV_LINKAGE gcov_type
53b2c31f 525gcov_read_counter (void)
44359ced 526{
b4d48d67 527 gcov_type value;
9e0943d4 528 const gcov_unsigned_t *buffer = gcov_read_words (2);
44359ced 529
530 if (!buffer)
531 return 0;
b4d48d67 532 value = from_file (buffer[0]);
533 if (sizeof (value) > sizeof (gcov_unsigned_t))
534 value |= ((gcov_type) from_file (buffer[1])) << 32;
535 else if (buffer[1])
536 gcov_var.error = -1;
8a5df2ce 537
44359ced 538 return value;
539}
540
59048041 541/* Mangle filename path of BASE and output new allocated pointer with
542 mangled path. */
543
544char *
545mangle_path (char const *base)
546{
547 /* Convert '/' to '#', convert '..' to '^',
548 convert ':' to '~' on DOS based file system. */
549 const char *probe;
550 char *buffer = (char *)xmalloc (strlen (base) + 10);
551 char *ptr = buffer;
552
553#if HAVE_DOS_BASED_FILE_SYSTEM
554 if (base[0] && base[1] == ':')
555 {
556 ptr[0] = base[0];
557 ptr[1] = '~';
558 ptr += 2;
559 base += 2;
560 }
561#endif
562 for (; *base; base = probe)
563 {
564 size_t len;
565
566 for (probe = base; *probe; probe++)
567 if (*probe == '/')
568 break;
569 len = probe - base;
570 if (len == 2 && base[0] == '.' && base[1] == '.')
571 *ptr++ = '^';
572 else
573 {
574 memcpy (ptr, base, len);
575 ptr += len;
576 }
577 if (*probe)
578 {
579 *ptr++ = '#';
580 probe++;
581 }
582 }
583
584 /* Terminate the string. */
585 *ptr = '\0';
586
587 return buffer;
588}
589
e76f6040 590/* We need to expose the below function when compiling for gcov-tool. */
591
592#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
44359ced 593/* Read string from coverage file. Returns a pointer to a static
594 buffer, or NULL on empty string. You must copy the string before
595 calling another gcov function. */
596
597GCOV_LINKAGE const char *
53b2c31f 598gcov_read_string (void)
44359ced 599{
600 unsigned length = gcov_read_unsigned ();
48e1416a 601
44359ced 602 if (!length)
603 return 0;
604
9e0943d4 605 return (const char *) gcov_read_words (length);
44359ced 606}
3f2c2dd8 607#endif
44359ced 608
609GCOV_LINKAGE void
610gcov_read_summary (struct gcov_summary *summary)
611{
5860b185 612 summary->runs = gcov_read_unsigned ();
56621355 613 summary->sum_max = gcov_read_unsigned ();
44359ced 614}
615
e76f6040 616/* We need to expose the below function when compiling for gcov-tool. */
617
618#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
80abd9e4 619/* Reset to a known position. BASE should have been obtained from
620 gcov_position, LENGTH should be a record length. */
621
622GCOV_LINKAGE void
623gcov_sync (gcov_position_t base, gcov_unsigned_t length)
624{
f1919901 625 gcov_nonruntime_assert (gcov_var.mode > 0);
80abd9e4 626 base += length;
627 if (base - gcov_var.start <= gcov_var.length)
628 gcov_var.offset = base - gcov_var.start;
629 else
630 {
631 gcov_var.offset = gcov_var.length = 0;
9e0943d4 632 fseek (gcov_var.file, base << 2, SEEK_SET);
633 gcov_var.start = ftell (gcov_var.file) >> 2;
80abd9e4 634 }
635}
636#endif
637
638#if IN_LIBGCOV
f0b5f617 639/* Move to a given position in a gcov file. */
80abd9e4 640
641GCOV_LINKAGE void
642gcov_seek (gcov_position_t base)
643{
80abd9e4 644 if (gcov_var.offset)
645 gcov_write_block (gcov_var.offset);
421055ca 646 fseek (gcov_var.file, base << 2, SEEK_SET);
9e0943d4 647 gcov_var.start = ftell (gcov_var.file) >> 2;
80abd9e4 648}
649#endif
650
44359ced 651#if IN_GCOV > 0
652/* Return the modification time of the current gcov file. */
653
654GCOV_LINKAGE time_t
53b2c31f 655gcov_time (void)
44359ced 656{
657 struct stat status;
48e1416a 658
44359ced 659 if (fstat (fileno (gcov_var.file), &status))
660 return 0;
661 else
662 return status.st_mtime;
663}
664#endif /* IN_GCOV */