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>.
7 This file is part of GCC.
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
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
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.
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/>. */
28 /* Routines declared in gcov-io.h. This file should be #included by
29 another source file, after having #included gcov-io.h. */
32 static void gcov_write_block (unsigned);
33 static gcov_unsigned_t *gcov_write_words (unsigned);
35 static const gcov_unsigned_t *gcov_read_words (unsigned);
37 static void gcov_allocate (unsigned);
40 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
45 value = (value >> 16) | (value << 16);
46 value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
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 for modification. If MODE is >= 0 an existing file will be
55 opened, if possible, and if MODE is <= 0, a new file will be
56 created. Use MODE=0 to attempt to reopen an existing file and then
57 fall back on creating a new one. Return zero on failure, >0 on
58 opening an existing file and <0 on creating a new one. */
62 gcov_open (const char *name)
64 gcov_open (const char *name, int mode)
74 s_flock.l_type = F_WRLCK;
75 s_flock.l_whence = SEEK_SET;
77 s_flock.l_len = 0; /* Until EOF. */
78 s_flock.l_pid = getpid ();
81 gcc_assert (!gcov_var.file);
83 gcov_var.offset = gcov_var.length = 0;
84 gcov_var.overread = -1u;
91 fd = open (name, O_RDWR);
93 fd = open (name, O_RDWR | O_CREAT, 0666);
97 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
100 gcov_var.file = fdopen (fd, "r+b");
113 if (fstat (fd, &st) < 0)
115 fclose (gcov_var.file);
122 gcov_var.mode = mode * 2 + 1;
125 gcov_var.mode = mode * 2 + 1;
128 gcov_var.file = fopen (name, "r+b");
133 gcov_var.file = fopen (name, "w+b");
135 gcov_var.mode = mode * 2 + 1;
141 setbuf (gcov_var.file, (char *)0);
146 /* Close the current gcov file. Flushes data to disk. Returns nonzero
147 on failure or error flag set. */
155 if (gcov_var.offset && gcov_var.mode < 0)
156 gcov_write_block (gcov_var.offset);
158 fclose (gcov_var.file);
163 free (gcov_var.buffer);
168 return gcov_var.error;
172 /* Check if MAGIC is EXPECTED. Use it to determine endianness of the
173 file. Returns +1 for same endian, -1 for other endian and zero for
177 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
179 if (magic == expected)
181 magic = (magic >> 16) | (magic << 16);
182 magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
183 if (magic == expected)
194 gcov_allocate (unsigned length)
196 size_t new_size = gcov_var.alloc;
199 new_size = GCOV_BLOCK_SIZE;
203 gcov_var.alloc = new_size;
204 gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
209 /* Write out the current block, if needs be. */
212 gcov_write_block (unsigned size)
214 if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
216 gcov_var.start += size;
217 gcov_var.offset -= size;
220 /* Allocate space to write BYTES bytes to the gcov file. Return a
221 pointer to those bytes, or NULL on failure. */
223 static gcov_unsigned_t *
224 gcov_write_words (unsigned words)
226 gcov_unsigned_t *result;
228 gcc_assert (gcov_var.mode < 0);
230 if (gcov_var.offset >= GCOV_BLOCK_SIZE)
232 gcov_write_block (GCOV_BLOCK_SIZE);
235 gcc_assert (gcov_var.offset == 1);
236 memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
240 if (gcov_var.offset + words > gcov_var.alloc)
241 gcov_allocate (gcov_var.offset + words);
243 result = &gcov_var.buffer[gcov_var.offset];
244 gcov_var.offset += words;
249 /* Write unsigned VALUE to coverage file. Sets error flag
253 gcov_write_unsigned (gcov_unsigned_t value)
255 gcov_unsigned_t *buffer = gcov_write_words (1);
260 /* Write counter VALUE to coverage file. Sets error flag
265 gcov_write_counter (gcov_type value)
267 gcov_unsigned_t *buffer = gcov_write_words (2);
269 buffer[0] = (gcov_unsigned_t) value;
270 if (sizeof (value) > sizeof (gcov_unsigned_t))
271 buffer[1] = (gcov_unsigned_t) (value >> 32);
275 #endif /* IN_LIBGCOV */
278 /* Write STRING to coverage file. Sets error flag on file
279 error, overflow flag on overflow */
282 gcov_write_string (const char *string)
286 gcov_unsigned_t *buffer;
290 length = strlen (string);
291 alloc = (length + 4) >> 2;
294 buffer = gcov_write_words (1 + alloc);
298 memcpy (&buffer[1], string, length);
303 /* Write a tag TAG and reserve space for the record length. Return a
304 value to be used for gcov_write_length. */
306 GCOV_LINKAGE gcov_position_t
307 gcov_write_tag (gcov_unsigned_t tag)
309 gcov_position_t result = gcov_var.start + gcov_var.offset;
310 gcov_unsigned_t *buffer = gcov_write_words (2);
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
324 gcov_write_length (gcov_position_t position)
327 gcov_unsigned_t length;
328 gcov_unsigned_t *buffer;
330 gcc_assert (gcov_var.mode < 0);
331 gcc_assert (position + 2 <= gcov_var.start + gcov_var.offset);
332 gcc_assert (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];
337 if (gcov_var.offset >= GCOV_BLOCK_SIZE)
338 gcov_write_block (gcov_var.offset);
341 #else /* IN_LIBGCOV */
343 /* Write a tag TAG and length LENGTH. */
346 gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
348 gcov_unsigned_t *buffer = gcov_write_words (2);
354 /* Write a summary structure to the gcov file. Return nonzero on
358 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
361 const struct gcov_ctr_summary *csum;
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++)
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);
374 #endif /* IN_LIBGCOV */
378 /* Return a pointer to read BYTES bytes from the gcov file. Returns
379 NULL on failure (read past EOF). */
381 static const gcov_unsigned_t *
382 gcov_read_words (unsigned words)
384 const gcov_unsigned_t *result;
385 unsigned excess = gcov_var.length - gcov_var.offset;
387 gcc_assert (gcov_var.mode > 0);
390 gcov_var.start += gcov_var.offset;
394 gcc_assert (excess == 1);
395 memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
398 memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4);
401 gcov_var.length = excess;
403 gcc_assert (!gcov_var.length || gcov_var.length == 1);
404 excess = GCOV_BLOCK_SIZE;
406 if (gcov_var.length + words > gcov_var.alloc)
407 gcov_allocate (gcov_var.length + words);
408 excess = gcov_var.alloc - gcov_var.length;
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)
415 gcov_var.overread += words - gcov_var.length;
420 result = &gcov_var.buffer[gcov_var.offset];
421 gcov_var.offset += words;
425 /* Read unsigned value from a coverage file. Sets error flag on file
426 error, overflow flag on overflow */
428 GCOV_LINKAGE gcov_unsigned_t
429 gcov_read_unsigned (void)
431 gcov_unsigned_t value;
432 const gcov_unsigned_t *buffer = gcov_read_words (1);
436 value = from_file (buffer[0]);
440 /* Read counter value from a coverage file. Sets error flag on file
441 error, overflow flag on overflow */
443 GCOV_LINKAGE gcov_type
444 gcov_read_counter (void)
447 const gcov_unsigned_t *buffer = gcov_read_words (2);
451 value = from_file (buffer[0]);
452 if (sizeof (value) > sizeof (gcov_unsigned_t))
453 value |= ((gcov_type) from_file (buffer[1])) << 32;
460 /* Read string from coverage file. Returns a pointer to a static
461 buffer, or NULL on empty string. You must copy the string before
462 calling another gcov function. */
465 GCOV_LINKAGE const char *
466 gcov_read_string (void)
468 unsigned length = gcov_read_unsigned ();
473 return (const char *) gcov_read_words (length);
478 gcov_read_summary (struct gcov_summary *summary)
481 struct gcov_ctr_summary *csum;
483 summary->checksum = gcov_read_unsigned ();
484 for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
486 csum->num = gcov_read_unsigned ();
487 csum->runs = gcov_read_unsigned ();
488 csum->sum_all = gcov_read_counter ();
489 csum->run_max = gcov_read_counter ();
490 csum->sum_max = gcov_read_counter ();
495 /* Reset to a known position. BASE should have been obtained from
496 gcov_position, LENGTH should be a record length. */
499 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
501 gcc_assert (gcov_var.mode > 0);
503 if (base - gcov_var.start <= gcov_var.length)
504 gcov_var.offset = base - gcov_var.start;
507 gcov_var.offset = gcov_var.length = 0;
508 fseek (gcov_var.file, base << 2, SEEK_SET);
509 gcov_var.start = ftell (gcov_var.file) >> 2;
515 /* Move to a given position in a gcov file. */
518 gcov_seek (gcov_position_t base)
520 gcc_assert (gcov_var.mode < 0);
522 gcov_write_block (gcov_var.offset);
523 fseek (gcov_var.file, base << 2, SEEK_SET);
524 gcov_var.start = ftell (gcov_var.file) >> 2;
529 /* Return the modification time of the current gcov file. */
536 if (fstat (fileno (gcov_var.file), &status))
539 return status.st_mtime;