Fix a bug that broke -freorder-functions
[official-gcc.git] / gcc / gcov-io.c
blob37c1c3e35084ba75ad8d88afaa7e25d4c0a87947
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
12 version.
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.
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. */
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
40 static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
42 #if !IN_LIBGCOV
43 if (gcov_var.endian)
45 value = (value >> 16) | (value << 16);
46 value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
48 #endif
49 return value;
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. */
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
69 #if IN_LIBGCOV
70 const int mode = 0;
71 #endif
72 #if GCOV_LOCKED
73 struct flock s_flock;
74 int fd;
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
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)
93 /* Read-only mode - acquire a read-lock. */
94 s_flock.l_type = F_RDLCK;
95 fd = open (name, O_RDONLY);
97 else
99 /* Write mode - acquire a write-lock. */
100 s_flock.l_type = F_WRLCK;
101 fd = open (name, O_RDWR | O_CREAT, 0666);
103 if (fd < 0)
104 return 0;
106 while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
107 continue;
109 gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
111 if (!gcov_var.file)
113 close (fd);
114 return 0;
117 if (mode > 0)
118 gcov_var.mode = 1;
119 else if (mode == 0)
121 struct stat st;
123 if (fstat (fd, &st) < 0)
125 fclose (gcov_var.file);
126 gcov_var.file = 0;
127 return 0;
129 if (st.st_size != 0)
130 gcov_var.mode = 1;
131 else
132 gcov_var.mode = mode * 2 + 1;
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");
140 if (gcov_var.file)
141 gcov_var.mode = 1;
142 else if (mode <= 0)
144 gcov_var.file = fopen (name, "w+b");
145 if (gcov_var.file)
146 gcov_var.mode = mode * 2 + 1;
148 if (!gcov_var.file)
149 return 0;
150 #endif
152 setbuf (gcov_var.file, (char *)0);
154 return 1;
157 /* Close the current gcov file. Flushes data to disk. Returns nonzero
158 on failure or error flag set. */
160 GCOV_LINKAGE int
161 gcov_close (void)
163 if (gcov_var.file)
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;
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;
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. */
187 GCOV_LINKAGE int
188 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
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)
196 gcov_var.endian = 1;
197 return -1;
199 return 0;
201 #endif
203 #if !IN_LIBGCOV
204 static void
205 gcov_allocate (unsigned length)
207 size_t new_size = gcov_var.alloc;
209 if (!new_size)
210 new_size = GCOV_BLOCK_SIZE;
211 new_size += length;
212 new_size *= 2;
214 gcov_var.alloc = new_size;
215 gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
217 #endif
219 #if !IN_GCOV
220 /* Write out the current block, if needs be. */
222 static void
223 gcov_write_block (unsigned size)
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;
231 /* Allocate space to write BYTES bytes to the gcov file. Return a
232 pointer to those bytes, or NULL on failure. */
234 static gcov_unsigned_t *
235 gcov_write_words (unsigned words)
237 gcov_unsigned_t *result;
239 gcc_assert (gcov_var.mode < 0);
240 #if IN_LIBGCOV
241 if (gcov_var.offset >= GCOV_BLOCK_SIZE)
243 gcov_write_block (GCOV_BLOCK_SIZE);
244 if (gcov_var.offset)
246 gcc_assert (gcov_var.offset == 1);
247 memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
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;
257 return result;
260 /* Write unsigned VALUE to coverage file. Sets error flag
261 appropriately. */
263 GCOV_LINKAGE void
264 gcov_write_unsigned (gcov_unsigned_t value)
266 gcov_unsigned_t *buffer = gcov_write_words (1);
268 buffer[0] = value;
271 /* Write counter VALUE to coverage file. Sets error flag
272 appropriately. */
274 #if IN_LIBGCOV
275 GCOV_LINKAGE void
276 gcov_write_counter (gcov_type value)
278 gcov_unsigned_t *buffer = gcov_write_words (2);
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;
286 #endif /* IN_LIBGCOV */
288 #if !IN_LIBGCOV
289 /* Write STRING to coverage file. Sets error flag on file
290 error, overflow flag on overflow */
292 GCOV_LINKAGE void
293 gcov_write_string (const char *string)
295 unsigned length = 0;
296 unsigned alloc = 0;
297 gcov_unsigned_t *buffer;
299 if (string)
301 length = strlen (string);
302 alloc = (length + 4) >> 2;
305 buffer = gcov_write_words (1 + alloc);
307 buffer[0] = alloc;
308 buffer[alloc] = 0;
309 memcpy (&buffer[1], string, length);
311 #endif
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. */
317 GCOV_LINKAGE gcov_position_t
318 gcov_write_tag (gcov_unsigned_t tag)
320 gcov_position_t result = gcov_var.start + gcov_var.offset;
321 gcov_unsigned_t *buffer = gcov_write_words (2);
323 buffer[0] = tag;
324 buffer[1] = 0;
326 return result;
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. */
334 GCOV_LINKAGE void
335 gcov_write_length (gcov_position_t position)
337 unsigned offset;
338 gcov_unsigned_t length;
339 gcov_unsigned_t *buffer;
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);
352 #else /* IN_LIBGCOV */
354 /* Write a tag TAG and length LENGTH. */
356 GCOV_LINKAGE void
357 gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
359 gcov_unsigned_t *buffer = gcov_write_words (2);
361 buffer[0] = tag;
362 buffer[1] = length;
365 /* Write a summary structure to the gcov file. Return nonzero on
366 overflow. */
368 GCOV_LINKAGE void
369 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
371 unsigned ix;
372 const struct gcov_ctr_summary *csum;
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++)
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);
385 #endif /* IN_LIBGCOV */
387 #endif /*!IN_GCOV */
389 /* Return a pointer to read BYTES bytes from the gcov file. Returns
390 NULL on failure (read past EOF). */
392 static const gcov_unsigned_t *
393 gcov_read_words (unsigned words)
395 const gcov_unsigned_t *result;
396 unsigned excess = gcov_var.length - gcov_var.offset;
398 gcc_assert (gcov_var.mode > 0);
399 if (excess < words)
401 gcov_var.start += gcov_var.offset;
402 #if IN_LIBGCOV
403 if (excess)
405 gcc_assert (excess == 1);
406 memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
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)
426 gcov_var.overread += words - gcov_var.length;
427 gcov_var.length = 0;
428 return 0;
431 result = &gcov_var.buffer[gcov_var.offset];
432 gcov_var.offset += words;
433 return result;
436 /* Read unsigned value from a coverage file. Sets error flag on file
437 error, overflow flag on overflow */
439 GCOV_LINKAGE gcov_unsigned_t
440 gcov_read_unsigned (void)
442 gcov_unsigned_t value;
443 const gcov_unsigned_t *buffer = gcov_read_words (1);
445 if (!buffer)
446 return 0;
447 value = from_file (buffer[0]);
448 return value;
451 /* Read counter value from a coverage file. Sets error flag on file
452 error, overflow flag on overflow */
454 GCOV_LINKAGE gcov_type
455 gcov_read_counter (void)
457 gcov_type value;
458 const gcov_unsigned_t *buffer = gcov_read_words (2);
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;
468 return value;
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. */
475 #if !IN_LIBGCOV
476 GCOV_LINKAGE const char *
477 gcov_read_string (void)
479 unsigned length = gcov_read_unsigned ();
481 if (!length)
482 return 0;
484 return (const char *) gcov_read_words (length);
486 #endif
488 GCOV_LINKAGE void
489 gcov_read_summary (struct gcov_summary *summary)
491 unsigned ix;
492 struct gcov_ctr_summary *csum;
494 summary->checksum = gcov_read_unsigned ();
495 for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
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 ();
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. */
509 GCOV_LINKAGE void
510 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
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
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;
523 #endif
525 #if IN_LIBGCOV
526 /* Move to a given position in a gcov file. */
528 GCOV_LINKAGE void
529 gcov_seek (gcov_position_t base)
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;
537 #endif
539 #if IN_GCOV > 0
540 /* Return the modification time of the current gcov file. */
542 GCOV_LINKAGE time_t
543 gcov_time (void)
545 struct stat status;
547 if (fstat (fileno (gcov_var.file), &status))
548 return 0;
549 else
550 return status.st_mtime;
552 #endif /* IN_GCOV */