]>
Commit | Line | Data |
---|---|---|
ca29da43 NS |
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 | ||
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 2, 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 | You should have received a copy of the GNU General Public License | |
20 | along with GCC; see the file COPYING. If not, write to the Free | |
21 | Software Foundation, 59 Temple Place - Suite 330, Boston, MA | |
22 | 02111-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 | ||
7d63a2fa NS |
27 | #if !IN_GCOV |
28 | static void gcov_write_block (unsigned); | |
29 | static unsigned char *gcov_write_bytes (unsigned); | |
30 | #endif | |
31 | static const unsigned char *gcov_read_bytes (unsigned); | |
32 | #if !IN_LIBGCOV | |
33 | static void gcov_allocate (unsigned); | |
34 | #endif | |
35 | ||
ca29da43 NS |
36 | /* Open a gcov file. NAME is the name of the file to open and MODE |
37 | indicates whether a new file should be created, or an existing file | |
38 | opened for modification. If MODE is >= 0 an existing file will be | |
39 | opened, if possible, and if MODE is <= 0, a new file will be | |
40 | created. Use MODE=0 to attempt to reopen an existing file and then | |
41 | fall back on creating a new one. Return zero on failure, >0 on | |
42 | opening an existing file and <0 on creating a new one. */ | |
43 | ||
44 | GCOV_LINKAGE int | |
45 | gcov_open (const char *name, int mode) | |
46 | { | |
47 | int result = 1; | |
474f141e | 48 | #if GCOV_LOCKED |
ca29da43 NS |
49 | struct flock s_flock; |
50 | ||
51 | s_flock.l_type = F_WRLCK; | |
52 | s_flock.l_whence = SEEK_SET; | |
53 | s_flock.l_start = 0; | |
54 | s_flock.l_len = 0; /* Until EOF. */ | |
55 | s_flock.l_pid = getpid (); | |
56 | #endif | |
57 | ||
58 | if (gcov_var.file) | |
59 | abort (); | |
7d63a2fa NS |
60 | gcov_var.start = 0; |
61 | gcov_var.offset = gcov_var.length = 0; | |
62 | gcov_var.overread = -4u; | |
63 | gcov_var.error = 0; | |
ca29da43 NS |
64 | if (mode >= 0) |
65 | gcov_var.file = fopen (name, "r+b"); | |
7d63a2fa NS |
66 | if (gcov_var.file) |
67 | gcov_var.mode = 1; | |
68 | else if (mode <= 0) | |
ca29da43 NS |
69 | { |
70 | result = -1; | |
71 | gcov_var.file = fopen (name, "w+b"); | |
7d63a2fa NS |
72 | if (gcov_var.file) |
73 | gcov_var.mode = -1; | |
ca29da43 NS |
74 | } |
75 | if (!gcov_var.file) | |
76 | return 0; | |
77 | ||
7d63a2fa NS |
78 | setbuf (gcov_var.file, (char *)0); |
79 | ||
474f141e | 80 | #if GCOV_LOCKED |
ca29da43 NS |
81 | while (fcntl (fileno (gcov_var.file), F_SETLKW, &s_flock) |
82 | && errno == EINTR) | |
83 | continue; | |
84 | #endif | |
85 | ||
ca29da43 NS |
86 | return result; |
87 | } | |
88 | ||
89 | /* Close the current gcov file. Flushes data to disk. Returns nonzero | |
90 | on failure or error flag set. */ | |
91 | ||
92 | GCOV_LINKAGE int | |
93 | gcov_close () | |
94 | { | |
ca29da43 NS |
95 | if (gcov_var.file) |
96 | { | |
7d63a2fa NS |
97 | #if !IN_GCOV |
98 | if (gcov_var.offset && gcov_var.mode < 0) | |
99 | gcov_write_block (gcov_var.offset); | |
100 | #endif | |
ca29da43 NS |
101 | fclose (gcov_var.file); |
102 | gcov_var.file = 0; | |
103 | gcov_var.length = 0; | |
104 | } | |
105 | #if !IN_LIBGCOV | |
106 | free (gcov_var.buffer); | |
107 | gcov_var.alloc = 0; | |
108 | gcov_var.buffer = 0; | |
109 | #endif | |
7d63a2fa NS |
110 | gcov_var.mode = 0; |
111 | return gcov_var.error; | |
112 | } | |
113 | ||
114 | #if !IN_LIBGCOV | |
115 | static void | |
116 | gcov_allocate (unsigned length) | |
117 | { | |
118 | size_t new_size = gcov_var.alloc; | |
119 | ||
120 | if (!new_size) | |
121 | new_size = GCOV_BLOCK_SIZE; | |
122 | new_size += length; | |
123 | new_size *= 2; | |
124 | ||
125 | gcov_var.alloc = new_size; | |
126 | gcov_var.buffer = xrealloc (gcov_var.buffer, new_size); | |
ca29da43 | 127 | } |
7d63a2fa | 128 | #endif |
ca29da43 NS |
129 | |
130 | #if !IN_GCOV | |
7d63a2fa NS |
131 | /* Write out the current block, if needs be. */ |
132 | ||
133 | static void | |
134 | gcov_write_block (unsigned size) | |
135 | { | |
136 | if (fwrite (gcov_var.buffer, size, 1, gcov_var.file) != 1) | |
137 | gcov_var.error = 1; | |
138 | gcov_var.start += size; | |
139 | gcov_var.offset -= size; | |
140 | } | |
141 | ||
ca29da43 NS |
142 | /* Allocate space to write BYTES bytes to the gcov file. Return a |
143 | pointer to those bytes, or NULL on failure. */ | |
144 | ||
7d63a2fa | 145 | static unsigned char * |
ca29da43 NS |
146 | gcov_write_bytes (unsigned bytes) |
147 | { | |
148 | char unsigned *result; | |
149 | ||
7d63a2fa | 150 | GCOV_CHECK_WRITING (); |
ca29da43 | 151 | #if IN_LIBGCOV |
7d63a2fa NS |
152 | if (gcov_var.offset >= GCOV_BLOCK_SIZE) |
153 | { | |
154 | gcov_write_block (GCOV_BLOCK_SIZE); | |
155 | if (gcov_var.offset) | |
ca29da43 | 156 | { |
7d63a2fa NS |
157 | GCOV_CHECK (gcov_var.offset == 4); |
158 | memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4); | |
ca29da43 | 159 | } |
7d63a2fa | 160 | } |
ca29da43 | 161 | #else |
7d63a2fa NS |
162 | if (gcov_var.offset + bytes > gcov_var.alloc) |
163 | gcov_allocate (gcov_var.offset + bytes); | |
ca29da43 | 164 | #endif |
7d63a2fa NS |
165 | result = &gcov_var.buffer[gcov_var.offset]; |
166 | gcov_var.offset += bytes; | |
ca29da43 | 167 | |
ca29da43 NS |
168 | return result; |
169 | } | |
170 | ||
171 | /* Write unsigned VALUE to coverage file. Sets error flag | |
172 | appropriately. */ | |
173 | ||
174 | GCOV_LINKAGE void | |
9b514d25 | 175 | gcov_write_unsigned (gcov_unsigned_t value) |
ca29da43 NS |
176 | { |
177 | unsigned char *buffer = gcov_write_bytes (4); | |
178 | unsigned ix; | |
179 | ||
ca29da43 NS |
180 | for (ix = 4; ix--; ) |
181 | { | |
182 | buffer[ix] = value; | |
183 | value >>= 8; | |
184 | } | |
185 | if (sizeof (value) > 4 && value) | |
186 | gcov_var.error = -1; | |
187 | ||
188 | return; | |
189 | } | |
190 | ||
191 | /* Write counter VALUE to coverage file. Sets error flag | |
192 | appropriately. */ | |
193 | ||
194 | #if IN_LIBGCOV | |
195 | GCOV_LINKAGE void | |
196 | gcov_write_counter (gcov_type value) | |
197 | { | |
198 | unsigned char *buffer = gcov_write_bytes (8); | |
199 | unsigned ix; | |
200 | ||
ca29da43 NS |
201 | for (ix = 8; ix--; ) |
202 | { | |
203 | buffer[ix] = value; | |
204 | value >>= 8; | |
205 | } | |
206 | if ((sizeof (value) > 8 && value) || value < 0) | |
207 | gcov_var.error = -1; | |
208 | return; | |
209 | } | |
210 | #endif /* IN_LIBGCOV */ | |
211 | ||
796621e8 | 212 | #if !IN_LIBGCOV |
ca29da43 NS |
213 | /* Write STRING to coverage file. Sets error flag on file |
214 | error, overflow flag on overflow */ | |
215 | ||
216 | GCOV_LINKAGE void | |
217 | gcov_write_string (const char *string) | |
218 | { | |
219 | unsigned length = 0; | |
220 | unsigned pad = 0; | |
221 | unsigned rem = 0; | |
222 | unsigned char *buffer; | |
7d63a2fa NS |
223 | unsigned ix; |
224 | unsigned value; | |
ca29da43 NS |
225 | |
226 | if (string) | |
227 | { | |
228 | length = strlen (string); | |
229 | rem = 4 - (length & 3); | |
230 | } | |
231 | ||
232 | buffer = gcov_write_bytes (4 + length + rem); | |
7d63a2fa NS |
233 | |
234 | value = length; | |
235 | for (ix = 4; ix--; ) | |
ca29da43 | 236 | { |
7d63a2fa NS |
237 | buffer[ix] = value; |
238 | value >>= 8; | |
ca29da43 | 239 | } |
7d63a2fa NS |
240 | memcpy (buffer + 4, string, length); |
241 | memcpy (buffer + 4 + length, &pad, rem); | |
ca29da43 | 242 | } |
796621e8 | 243 | #endif |
ca29da43 | 244 | |
474f141e | 245 | #if !IN_LIBGCOV |
ca29da43 NS |
246 | /* Write a tag TAG and reserve space for the record length. Return a |
247 | value to be used for gcov_write_length. */ | |
248 | ||
9b514d25 NS |
249 | GCOV_LINKAGE gcov_position_t |
250 | gcov_write_tag (gcov_unsigned_t tag) | |
ca29da43 | 251 | { |
7d63a2fa | 252 | gcov_position_t result = gcov_var.start + gcov_var.offset; |
ca29da43 NS |
253 | unsigned char *buffer = gcov_write_bytes (8); |
254 | unsigned ix; | |
255 | ||
ca29da43 NS |
256 | for (ix = 4; ix--; ) |
257 | { | |
258 | buffer[ix] = tag; | |
259 | tag >>= 8; | |
260 | } | |
261 | memset (buffer + 4, 0, 4); | |
262 | return result; | |
263 | } | |
264 | ||
265 | /* Write a record length using POSITION, which was returned by | |
266 | gcov_write_tag. The current file position is the end of the | |
267 | record, and is restored before returning. Returns nonzero on | |
268 | overflow. */ | |
269 | ||
270 | GCOV_LINKAGE void | |
9b514d25 | 271 | gcov_write_length (gcov_position_t position) |
ca29da43 | 272 | { |
7d63a2fa NS |
273 | unsigned offset; |
274 | gcov_unsigned_t length; | |
275 | unsigned char *buffer; | |
276 | unsigned ix; | |
277 | ||
278 | GCOV_CHECK_WRITING (); | |
279 | GCOV_CHECK (position + 8 <= gcov_var.start + gcov_var.offset); | |
280 | GCOV_CHECK (position >= gcov_var.start); | |
281 | offset = position - gcov_var.start; | |
282 | length = gcov_var.offset - offset - 8; | |
283 | buffer = &gcov_var.buffer[offset + 4]; | |
284 | for (ix = 4; ix--; ) | |
ca29da43 | 285 | { |
7d63a2fa NS |
286 | buffer[ix] = length; |
287 | length >>= 8; | |
ca29da43 | 288 | } |
7d63a2fa NS |
289 | if (gcov_var.offset >= GCOV_BLOCK_SIZE) |
290 | gcov_write_block (gcov_var.offset); | |
ca29da43 | 291 | } |
9b514d25 NS |
292 | |
293 | #else /* IN_LIBGCOV */ | |
474f141e NS |
294 | |
295 | /* Write a tag TAG and length LENGTH. */ | |
296 | ||
297 | GCOV_LINKAGE void | |
9b514d25 | 298 | gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length) |
474f141e NS |
299 | { |
300 | unsigned char *buffer = gcov_write_bytes (8); | |
301 | unsigned ix; | |
302 | ||
474f141e NS |
303 | for (ix = 4; ix--; ) |
304 | { | |
305 | buffer[ix] = tag; | |
306 | tag >>= 8; | |
307 | } | |
308 | for (ix = 4; ix--; ) | |
309 | { | |
310 | buffer[ix + 4] = length; | |
311 | length >>= 8; | |
312 | } | |
313 | return; | |
314 | } | |
ca29da43 | 315 | |
ca29da43 NS |
316 | /* Write a summary structure to the gcov file. Return non-zero on |
317 | overflow. */ | |
318 | ||
319 | GCOV_LINKAGE void | |
9b514d25 | 320 | gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) |
ca29da43 | 321 | { |
cdb23767 NS |
322 | unsigned ix; |
323 | const struct gcov_ctr_summary *csum; | |
ca29da43 | 324 | |
474f141e | 325 | gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH); |
ca29da43 | 326 | gcov_write_unsigned (summary->checksum); |
9b514d25 | 327 | for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) |
cdb23767 NS |
328 | { |
329 | gcov_write_unsigned (csum->num); | |
330 | gcov_write_unsigned (csum->runs); | |
331 | gcov_write_counter (csum->sum_all); | |
332 | gcov_write_counter (csum->run_max); | |
333 | gcov_write_counter (csum->sum_max); | |
334 | } | |
ca29da43 NS |
335 | } |
336 | #endif /* IN_LIBGCOV */ | |
337 | ||
338 | #endif /*!IN_GCOV */ | |
339 | ||
340 | /* Return a pointer to read BYTES bytes from the gcov file. Returns | |
341 | NULL on failure (read past EOF). */ | |
342 | ||
7d63a2fa | 343 | static const unsigned char * |
ca29da43 NS |
344 | gcov_read_bytes (unsigned bytes) |
345 | { | |
346 | const unsigned char *result; | |
7d63a2fa | 347 | unsigned excess = gcov_var.length - gcov_var.offset; |
ca29da43 | 348 | |
7d63a2fa NS |
349 | GCOV_CHECK_READING (); |
350 | if (excess < bytes) | |
ca29da43 | 351 | { |
7d63a2fa NS |
352 | gcov_var.start += gcov_var.offset; |
353 | #if IN_LIBGCOV | |
354 | if (excess) | |
355 | { | |
356 | GCOV_CHECK (excess == 4); | |
357 | memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4); | |
358 | } | |
359 | #else | |
360 | memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess); | |
361 | #endif | |
362 | gcov_var.offset = 0; | |
363 | gcov_var.length = excess; | |
364 | #if IN_LIBGCOV | |
365 | GCOV_CHECK (!gcov_var.length || gcov_var.length == 4); | |
366 | excess = GCOV_BLOCK_SIZE; | |
367 | #else | |
368 | if (gcov_var.length + bytes > gcov_var.alloc) | |
369 | gcov_allocate (gcov_var.length + bytes); | |
370 | excess = gcov_var.alloc - gcov_var.length; | |
371 | #endif | |
372 | excess = fread (gcov_var.buffer + gcov_var.offset, | |
373 | 1, excess, gcov_var.file); | |
374 | gcov_var.length += excess; | |
375 | if (gcov_var.length < bytes) | |
376 | { | |
377 | gcov_var.overread += bytes - gcov_var.length; | |
378 | gcov_var.length = 0; | |
379 | return 0; | |
380 | } | |
ca29da43 | 381 | } |
7d63a2fa NS |
382 | result = &gcov_var.buffer[gcov_var.offset]; |
383 | gcov_var.offset += bytes; | |
ca29da43 NS |
384 | return result; |
385 | } | |
386 | ||
387 | /* Read unsigned value from a coverage file. Sets error flag on file | |
388 | error, overflow flag on overflow */ | |
389 | ||
9b514d25 | 390 | GCOV_LINKAGE gcov_unsigned_t |
ca29da43 NS |
391 | gcov_read_unsigned () |
392 | { | |
9b514d25 | 393 | gcov_unsigned_t value = 0; |
ca29da43 NS |
394 | unsigned ix; |
395 | const unsigned char *buffer = gcov_read_bytes (4); | |
396 | ||
397 | if (!buffer) | |
398 | return 0; | |
399 | for (ix = sizeof (value); ix < 4; ix++) | |
400 | if (buffer[ix]) | |
401 | gcov_var.error = -1; | |
402 | for (ix = 0; ix != 4; ix++) | |
403 | { | |
404 | value <<= 8; | |
405 | value |= buffer[ix]; | |
406 | } | |
407 | return value; | |
408 | } | |
409 | ||
410 | /* Read counter value from a coverage file. Sets error flag on file | |
411 | error, overflow flag on overflow */ | |
412 | ||
413 | GCOV_LINKAGE gcov_type | |
414 | gcov_read_counter () | |
415 | { | |
416 | gcov_type value = 0; | |
417 | unsigned ix; | |
418 | const unsigned char *buffer = gcov_read_bytes (8); | |
419 | ||
420 | if (!buffer) | |
421 | return 0; | |
422 | for (ix = sizeof (value); ix < 8; ix++) | |
423 | if (buffer[ix]) | |
424 | gcov_var.error = -1; | |
425 | for (ix = 0; ix != 8; ix++) | |
426 | { | |
427 | value <<= 8; | |
428 | value |= buffer[ix]; | |
429 | } | |
430 | if (value < 0) | |
431 | gcov_var.error = -1; | |
432 | return value; | |
433 | } | |
434 | ||
435 | /* Read string from coverage file. Returns a pointer to a static | |
436 | buffer, or NULL on empty string. You must copy the string before | |
437 | calling another gcov function. */ | |
438 | ||
796621e8 | 439 | #if !IN_LIBGCOV |
ca29da43 NS |
440 | GCOV_LINKAGE const char * |
441 | gcov_read_string () | |
442 | { | |
443 | unsigned length = gcov_read_unsigned (); | |
444 | ||
445 | if (!length) | |
446 | return 0; | |
447 | ||
448 | length += 4 - (length & 3); | |
449 | return (const char *) gcov_read_bytes (length); | |
450 | } | |
796621e8 | 451 | #endif |
ca29da43 NS |
452 | |
453 | GCOV_LINKAGE void | |
454 | gcov_read_summary (struct gcov_summary *summary) | |
455 | { | |
cdb23767 NS |
456 | unsigned ix; |
457 | struct gcov_ctr_summary *csum; | |
458 | ||
ca29da43 | 459 | summary->checksum = gcov_read_unsigned (); |
9b514d25 | 460 | for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) |
cdb23767 NS |
461 | { |
462 | csum->num = gcov_read_unsigned (); | |
463 | csum->runs = gcov_read_unsigned (); | |
464 | csum->sum_all = gcov_read_counter (); | |
465 | csum->run_max = gcov_read_counter (); | |
466 | csum->sum_max = gcov_read_counter (); | |
467 | } | |
ca29da43 NS |
468 | } |
469 | ||
7d63a2fa NS |
470 | #if !IN_LIBGCOV |
471 | /* Reset to a known position. BASE should have been obtained from | |
472 | gcov_position, LENGTH should be a record length. */ | |
473 | ||
474 | GCOV_LINKAGE void | |
475 | gcov_sync (gcov_position_t base, gcov_unsigned_t length) | |
476 | { | |
477 | GCOV_CHECK_READING (); | |
478 | base += length; | |
479 | if (base - gcov_var.start <= gcov_var.length) | |
480 | gcov_var.offset = base - gcov_var.start; | |
481 | else | |
482 | { | |
483 | gcov_var.offset = gcov_var.length = 0; | |
484 | fseek (gcov_var.file, base, SEEK_SET); | |
485 | gcov_var.start = ftell (gcov_var.file); | |
486 | } | |
487 | } | |
488 | #endif | |
489 | ||
490 | #if IN_LIBGCOV | |
491 | /* Move to the a set position in a gcov file. BASE is zero to move to | |
492 | the end, and non-zero to move to that position. */ | |
493 | ||
494 | GCOV_LINKAGE void | |
495 | gcov_seek (gcov_position_t base) | |
496 | { | |
497 | GCOV_CHECK_WRITING (); | |
498 | if (gcov_var.offset) | |
499 | gcov_write_block (gcov_var.offset); | |
500 | fseek (gcov_var.file, base, base ? SEEK_SET : SEEK_END); | |
501 | gcov_var.start = ftell (gcov_var.file); | |
502 | } | |
503 | #endif | |
504 | ||
ca29da43 NS |
505 | #if IN_GCOV > 0 |
506 | /* Return the modification time of the current gcov file. */ | |
507 | ||
508 | GCOV_LINKAGE time_t | |
509 | gcov_time () | |
510 | { | |
511 | struct stat status; | |
512 | ||
513 | if (fstat (fileno (gcov_var.file), &status)) | |
514 | return 0; | |
515 | else | |
516 | return status.st_mtime; | |
517 | } | |
518 | #endif /* IN_GCOV */ |