Merge branch 'vendor/MDOCML'
[dragonfly.git] / contrib / gcc-4.4 / 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 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.  */
59
60 GCOV_LINKAGE int
61 #if IN_LIBGCOV
62 gcov_open (const char *name)
63 #else
64 gcov_open (const char *name, int mode)
65 #endif
66 {
67 #if IN_LIBGCOV
68   const int mode = 0;
69 #endif
70 #if GCOV_LOCKED
71   struct flock s_flock;
72   int fd;
73
74   s_flock.l_type = F_WRLCK;
75   s_flock.l_whence = SEEK_SET;
76   s_flock.l_start = 0;
77   s_flock.l_len = 0; /* Until EOF.  */
78   s_flock.l_pid = getpid ();
79 #endif
80   
81   gcc_assert (!gcov_var.file);
82   gcov_var.start = 0;
83   gcov_var.offset = gcov_var.length = 0;
84   gcov_var.overread = -1u;
85   gcov_var.error = 0;
86 #if !IN_LIBGCOV
87   gcov_var.endian = 0;
88 #endif
89 #if GCOV_LOCKED
90   if (mode > 0)
91     fd = open (name, O_RDWR);
92   else
93     fd = open (name, O_RDWR | O_CREAT, 0666);
94   if (fd < 0)
95     return 0;
96
97   while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
98     continue;
99
100   gcov_var.file = fdopen (fd, "r+b");
101   if (!gcov_var.file)
102     {
103       close (fd);
104       return 0;
105     }
106
107   if (mode > 0)
108     gcov_var.mode = 1;
109   else if (mode == 0)
110     {
111       struct stat st;
112
113       if (fstat (fd, &st) < 0)
114         {
115           fclose (gcov_var.file);
116           gcov_var.file = 0;
117           return 0;
118         }
119       if (st.st_size != 0)
120         gcov_var.mode = 1;
121       else
122         gcov_var.mode = mode * 2 + 1;
123     }
124   else
125     gcov_var.mode = mode * 2 + 1;
126 #else
127   if (mode >= 0)
128     gcov_var.file = fopen (name, "r+b");
129   if (gcov_var.file)
130     gcov_var.mode = 1;
131   else if (mode <= 0)
132     {
133       gcov_var.file = fopen (name, "w+b");
134       if (gcov_var.file)
135         gcov_var.mode = mode * 2 + 1;
136     }
137   if (!gcov_var.file)
138     return 0;
139 #endif
140
141   setbuf (gcov_var.file, (char *)0);
142   
143   return 1;
144 }
145
146 /* Close the current gcov file. Flushes data to disk. Returns nonzero
147    on failure or error flag set.  */
148
149 GCOV_LINKAGE int
150 gcov_close (void)
151 {
152   if (gcov_var.file)
153     {
154 #if !IN_GCOV
155       if (gcov_var.offset && gcov_var.mode < 0)
156         gcov_write_block (gcov_var.offset);
157 #endif
158       fclose (gcov_var.file);
159       gcov_var.file = 0;
160       gcov_var.length = 0;
161     }
162 #if !IN_LIBGCOV
163   free (gcov_var.buffer);
164   gcov_var.alloc = 0;
165   gcov_var.buffer = 0;
166 #endif
167   gcov_var.mode = 0;
168   return gcov_var.error;
169 }
170
171 #if !IN_LIBGCOV
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
174    not EXPECTED.  */
175
176 GCOV_LINKAGE int
177 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
178 {
179   if (magic == expected)
180     return 1;
181   magic = (magic >> 16) | (magic << 16);
182   magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
183   if (magic == expected)
184     {
185       gcov_var.endian = 1;
186       return -1;
187     }
188   return 0;
189 }
190 #endif
191
192 #if !IN_LIBGCOV
193 static void
194 gcov_allocate (unsigned length)
195 {
196   size_t new_size = gcov_var.alloc;
197   
198   if (!new_size)
199     new_size = GCOV_BLOCK_SIZE;
200   new_size += length;
201   new_size *= 2;
202   
203   gcov_var.alloc = new_size;
204   gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
205 }
206 #endif
207
208 #if !IN_GCOV
209 /* Write out the current block, if needs be.  */
210
211 static void
212 gcov_write_block (unsigned size)
213 {
214   if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
215     gcov_var.error = 1;
216   gcov_var.start += size;
217   gcov_var.offset -= size;
218 }
219
220 /* Allocate space to write BYTES bytes to the gcov file. Return a
221    pointer to those bytes, or NULL on failure.  */
222
223 static gcov_unsigned_t *
224 gcov_write_words (unsigned words)
225 {
226   gcov_unsigned_t *result;
227
228   gcc_assert (gcov_var.mode < 0);
229 #if IN_LIBGCOV
230   if (gcov_var.offset >= GCOV_BLOCK_SIZE)
231     {
232       gcov_write_block (GCOV_BLOCK_SIZE);
233       if (gcov_var.offset)
234         {
235           gcc_assert (gcov_var.offset == 1);
236           memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
237         }
238     }
239 #else
240   if (gcov_var.offset + words > gcov_var.alloc)
241     gcov_allocate (gcov_var.offset + words);
242 #endif
243   result = &gcov_var.buffer[gcov_var.offset];
244   gcov_var.offset += words;
245   
246   return result;
247 }
248
249 /* Write unsigned VALUE to coverage file.  Sets error flag
250    appropriately.  */
251
252 GCOV_LINKAGE void
253 gcov_write_unsigned (gcov_unsigned_t value)
254 {
255   gcov_unsigned_t *buffer = gcov_write_words (1);
256
257   buffer[0] = value;
258 }
259
260 /* Write counter VALUE to coverage file.  Sets error flag
261    appropriately.  */
262
263 #if IN_LIBGCOV
264 GCOV_LINKAGE void
265 gcov_write_counter (gcov_type value)
266 {
267   gcov_unsigned_t *buffer = gcov_write_words (2);
268
269   buffer[0] = (gcov_unsigned_t) value;
270   if (sizeof (value) > sizeof (gcov_unsigned_t))
271     buffer[1] = (gcov_unsigned_t) (value >> 32);
272   else
273     buffer[1] = 0;
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
281 GCOV_LINKAGE void
282 gcov_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
306 GCOV_LINKAGE gcov_position_t
307 gcov_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
323 GCOV_LINKAGE void
324 gcov_write_length (gcov_position_t position)
325 {
326   unsigned offset;
327   gcov_unsigned_t length;
328   gcov_unsigned_t *buffer;
329
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];
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
345 GCOV_LINKAGE void
346 gcov_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
357 GCOV_LINKAGE void
358 gcov_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
381 static const gcov_unsigned_t *
382 gcov_read_words (unsigned words)
383 {
384   const gcov_unsigned_t *result;
385   unsigned excess = gcov_var.length - gcov_var.offset;
386   
387   gcc_assert (gcov_var.mode > 0);
388   if (excess < words)
389     {
390       gcov_var.start += gcov_var.offset;
391 #if IN_LIBGCOV
392       if (excess)
393         {
394           gcc_assert (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       gcc_assert (!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
428 GCOV_LINKAGE gcov_unsigned_t
429 gcov_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
443 GCOV_LINKAGE gcov_type
444 gcov_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   return value;
458 }
459
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.  */
463
464 #if !IN_LIBGCOV
465 GCOV_LINKAGE const char *
466 gcov_read_string (void)
467 {
468   unsigned length = gcov_read_unsigned ();
469   
470   if (!length)
471     return 0;
472
473   return (const char *) gcov_read_words (length);
474 }
475 #endif
476
477 GCOV_LINKAGE void
478 gcov_read_summary (struct gcov_summary *summary)
479 {
480   unsigned ix;
481   struct gcov_ctr_summary *csum;
482   
483   summary->checksum = gcov_read_unsigned ();
484   for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
485     {
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 ();
491     }
492 }
493
494 #if !IN_LIBGCOV
495 /* Reset to a known position.  BASE should have been obtained from
496    gcov_position, LENGTH should be a record length.  */
497
498 GCOV_LINKAGE void
499 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
500 {
501   gcc_assert (gcov_var.mode > 0);
502   base += length;
503   if (base - gcov_var.start <= gcov_var.length)
504     gcov_var.offset = base - gcov_var.start;
505   else
506     {
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;
510     }
511 }
512 #endif
513
514 #if IN_LIBGCOV
515 /* Move to a given position in a gcov file.  */
516
517 GCOV_LINKAGE void
518 gcov_seek (gcov_position_t base)
519 {
520   gcc_assert (gcov_var.mode < 0);
521   if (gcov_var.offset)
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;
525 }
526 #endif
527
528 #if IN_GCOV > 0
529 /* Return the modification time of the current gcov file.  */
530
531 GCOV_LINKAGE time_t
532 gcov_time (void)
533 {
534   struct stat status;
535   
536   if (fstat (fileno (gcov_var.file), &status))
537     return 0;
538   else
539     return status.st_mtime;
540 }
541 #endif /* IN_GCOV */