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