OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / gcov-io.c
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 */