]>
Commit | Line | Data |
---|---|---|
ca29da43 | 1 | /* File format for coverage information |
8d9254fc | 2 | Copyright (C) 1996-2020 Free Software Foundation, Inc. |
ca29da43 NS |
3 | Contributed by Bob Manson <manson@cygnus.com>. |
4 | Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. | |
5 | ||
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
9dcd6f09 | 10 | Software Foundation; either version 3, or (at your option) any later |
ca29da43 NS |
11 | version. |
12 | ||
13 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
ad01c437 JM |
18 | Under Section 7 of GPL version 3, you are granted additional |
19 | permissions described in the GCC Runtime Library Exception, version | |
20 | 3.1, as published by the Free Software Foundation. | |
21 | ||
22 | You should have received a copy of the GNU General Public License and | |
23 | a copy of the GCC Runtime Library Exception along with this program; | |
24 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
9dcd6f09 | 25 | <http://www.gnu.org/licenses/>. */ |
ca29da43 NS |
26 | |
27 | /* Routines declared in gcov-io.h. This file should be #included by | |
28 | another source file, after having #included gcov-io.h. */ | |
29 | ||
7d63a2fa NS |
30 | #if !IN_GCOV |
31 | static void gcov_write_block (unsigned); | |
330d2e2a | 32 | static gcov_unsigned_t *gcov_write_words (unsigned); |
7d63a2fa | 33 | #endif |
330d2e2a | 34 | static const gcov_unsigned_t *gcov_read_words (unsigned); |
7d63a2fa NS |
35 | #if !IN_LIBGCOV |
36 | static void gcov_allocate (unsigned); | |
37 | #endif | |
38 | ||
40d6b753 RX |
39 | /* Optimum number of gcov_unsigned_t's read from or written to disk. */ |
40 | #define GCOV_BLOCK_SIZE (1 << 10) | |
41 | ||
88d67744 | 42 | struct gcov_var |
40d6b753 RX |
43 | { |
44 | FILE *file; | |
45 | gcov_position_t start; /* Position of first byte of block */ | |
46 | unsigned offset; /* Read/write position within the block. */ | |
47 | unsigned length; /* Read limit in the block. */ | |
48 | unsigned overread; /* Number of words overread. */ | |
49 | int error; /* < 0 overflow, > 0 disk error. */ | |
50 | int mode; /* < 0 writing, > 0 reading */ | |
17d1594b | 51 | int endian; /* Swap endianness. */ |
40d6b753 RX |
52 | #if IN_LIBGCOV |
53 | /* Holds one block plus 4 bytes, thus all coverage reads & writes | |
54 | fit within this buffer and we always can transfer GCOV_BLOCK_SIZE | |
55 | to and from the disk. libgcov never backtracks and only writes 4 | |
56 | or 8 byte objects. */ | |
57 | gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1]; | |
58 | #else | |
40d6b753 RX |
59 | /* Holds a variable length block, as the compiler can write |
60 | strings and needs to backtrack. */ | |
61 | size_t alloc; | |
62 | gcov_unsigned_t *buffer; | |
63 | #endif | |
64 | } gcov_var; | |
65 | ||
66 | /* Save the current position in the gcov file. */ | |
c77556a5 RX |
67 | /* We need to expose this function when compiling for gcov-tool. */ |
68 | #ifndef IN_GCOV_TOOL | |
69 | static inline | |
70 | #endif | |
71 | gcov_position_t | |
40d6b753 RX |
72 | gcov_position (void) |
73 | { | |
e3f0315f | 74 | gcov_nonruntime_assert (gcov_var.mode > 0); |
40d6b753 RX |
75 | return gcov_var.start + gcov_var.offset; |
76 | } | |
77 | ||
78 | /* Return nonzero if the error flag is set. */ | |
c77556a5 RX |
79 | /* We need to expose this function when compiling for gcov-tool. */ |
80 | #ifndef IN_GCOV_TOOL | |
81 | static inline | |
82 | #endif | |
83 | int | |
40d6b753 RX |
84 | gcov_is_error (void) |
85 | { | |
86 | return gcov_var.file ? gcov_var.error : 1; | |
87 | } | |
88 | ||
89 | #if IN_LIBGCOV | |
90 | /* Move to beginning of file and initialize for writing. */ | |
91 | GCOV_LINKAGE inline void | |
92 | gcov_rewrite (void) | |
93 | { | |
40d6b753 RX |
94 | gcov_var.mode = -1; |
95 | gcov_var.start = 0; | |
96 | gcov_var.offset = 0; | |
97 | fseek (gcov_var.file, 0L, SEEK_SET); | |
98 | } | |
99 | #endif | |
100 | ||
160e2e4f NS |
101 | static inline gcov_unsigned_t from_file (gcov_unsigned_t value) |
102 | { | |
17d1594b | 103 | #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) |
160e2e4f NS |
104 | if (gcov_var.endian) |
105 | { | |
106 | value = (value >> 16) | (value << 16); | |
107 | value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff); | |
108 | } | |
109 | #endif | |
110 | return value; | |
111 | } | |
112 | ||
ca29da43 NS |
113 | /* Open a gcov file. NAME is the name of the file to open and MODE |
114 | indicates whether a new file should be created, or an existing file | |
ad467730 NV |
115 | opened. If MODE is >= 0 an existing file will be opened, if |
116 | possible, and if MODE is <= 0, a new file will be created. Use | |
117 | MODE=0 to attempt to reopen an existing file and then fall back on | |
fc0911e0 | 118 | creating a new one. If MODE > 0, the file will be opened in |
ad467730 | 119 | read-only mode. Otherwise it will be opened for modification. |
fc0911e0 | 120 | Return zero on failure, non-zero on success. */ |
ca29da43 NS |
121 | |
122 | GCOV_LINKAGE int | |
160e2e4f NS |
123 | #if IN_LIBGCOV |
124 | gcov_open (const char *name) | |
125 | #else | |
ca29da43 | 126 | gcov_open (const char *name, int mode) |
160e2e4f | 127 | #endif |
ca29da43 | 128 | { |
160e2e4f | 129 | #if IN_LIBGCOV |
74d2b907 | 130 | int mode = 0; |
160e2e4f | 131 | #endif |
474f141e | 132 | #if GCOV_LOCKED |
ca29da43 | 133 | struct flock s_flock; |
c2cd64b5 | 134 | int fd; |
ca29da43 | 135 | |
ca29da43 NS |
136 | s_flock.l_whence = SEEK_SET; |
137 | s_flock.l_start = 0; | |
138 | s_flock.l_len = 0; /* Until EOF. */ | |
139 | s_flock.l_pid = getpid (); | |
140 | #endif | |
b8698a0f | 141 | |
e3f0315f | 142 | gcov_nonruntime_assert (!gcov_var.file); |
7d63a2fa NS |
143 | gcov_var.start = 0; |
144 | gcov_var.offset = gcov_var.length = 0; | |
330d2e2a | 145 | gcov_var.overread = -1u; |
7d63a2fa | 146 | gcov_var.error = 0; |
de8bfcc8 | 147 | #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) |
160e2e4f NS |
148 | gcov_var.endian = 0; |
149 | #endif | |
c2cd64b5 JJ |
150 | #if GCOV_LOCKED |
151 | if (mode > 0) | |
ad467730 NV |
152 | { |
153 | /* Read-only mode - acquire a read-lock. */ | |
154 | s_flock.l_type = F_RDLCK; | |
2588b26e RM |
155 | /* pass mode (ignored) for compatibility */ |
156 | fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR); | |
ad467730 | 157 | } |
fc0911e0 | 158 | else |
55428cc3 LA |
159 | { |
160 | /* Write mode - acquire a write-lock. */ | |
161 | s_flock.l_type = F_WRLCK; | |
fc0911e0 NS |
162 | /* Truncate if force new mode. */ |
163 | fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666); | |
ad467730 | 164 | } |
c2cd64b5 JJ |
165 | if (fd < 0) |
166 | return 0; | |
167 | ||
168 | while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR) | |
169 | continue; | |
170 | ||
ad467730 NV |
171 | gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b"); |
172 | ||
c2cd64b5 JJ |
173 | if (!gcov_var.file) |
174 | { | |
175 | close (fd); | |
176 | return 0; | |
177 | } | |
c2cd64b5 | 178 | #else |
ca29da43 | 179 | if (mode >= 0) |
fc0911e0 | 180 | /* Open an existing file. */ |
ad467730 NV |
181 | gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b"); |
182 | ||
7d63a2fa | 183 | if (gcov_var.file) |
fc0911e0 | 184 | mode = 1; |
7d63a2fa | 185 | else if (mode <= 0) |
fc0911e0 NS |
186 | /* Create a new file. */ |
187 | gcov_var.file = fopen (name, "w+b"); | |
188 | ||
ca29da43 NS |
189 | if (!gcov_var.file) |
190 | return 0; | |
ca29da43 NS |
191 | #endif |
192 | ||
fc0911e0 NS |
193 | gcov_var.mode = mode ? mode : 1; |
194 | ||
c2cd64b5 | 195 | setbuf (gcov_var.file, (char *)0); |
b8698a0f | 196 | |
160e2e4f | 197 | return 1; |
ca29da43 NS |
198 | } |
199 | ||
200 | /* Close the current gcov file. Flushes data to disk. Returns nonzero | |
201 | on failure or error flag set. */ | |
202 | ||
203 | GCOV_LINKAGE int | |
82a30d6f | 204 | gcov_close (void) |
ca29da43 | 205 | { |
ca29da43 NS |
206 | if (gcov_var.file) |
207 | { | |
7d63a2fa NS |
208 | #if !IN_GCOV |
209 | if (gcov_var.offset && gcov_var.mode < 0) | |
210 | gcov_write_block (gcov_var.offset); | |
211 | #endif | |
ca29da43 NS |
212 | fclose (gcov_var.file); |
213 | gcov_var.file = 0; | |
214 | gcov_var.length = 0; | |
215 | } | |
216 | #if !IN_LIBGCOV | |
217 | free (gcov_var.buffer); | |
218 | gcov_var.alloc = 0; | |
219 | gcov_var.buffer = 0; | |
220 | #endif | |
7d63a2fa NS |
221 | gcov_var.mode = 0; |
222 | return gcov_var.error; | |
223 | } | |
224 | ||
17d1594b | 225 | #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) |
160e2e4f NS |
226 | /* Check if MAGIC is EXPECTED. Use it to determine endianness of the |
227 | file. Returns +1 for same endian, -1 for other endian and zero for | |
228 | not EXPECTED. */ | |
229 | ||
230 | GCOV_LINKAGE int | |
231 | gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected) | |
232 | { | |
233 | if (magic == expected) | |
234 | return 1; | |
235 | magic = (magic >> 16) | (magic << 16); | |
236 | magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff); | |
237 | if (magic == expected) | |
238 | { | |
239 | gcov_var.endian = 1; | |
240 | return -1; | |
241 | } | |
242 | return 0; | |
243 | } | |
244 | #endif | |
245 | ||
7d63a2fa NS |
246 | #if !IN_LIBGCOV |
247 | static void | |
248 | gcov_allocate (unsigned length) | |
249 | { | |
250 | size_t new_size = gcov_var.alloc; | |
b8698a0f | 251 | |
7d63a2fa NS |
252 | if (!new_size) |
253 | new_size = GCOV_BLOCK_SIZE; | |
254 | new_size += length; | |
255 | new_size *= 2; | |
b8698a0f | 256 | |
7d63a2fa | 257 | gcov_var.alloc = new_size; |
1b4572a8 | 258 | gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2); |
ca29da43 | 259 | } |
7d63a2fa | 260 | #endif |
ca29da43 NS |
261 | |
262 | #if !IN_GCOV | |
7d63a2fa NS |
263 | /* Write out the current block, if needs be. */ |
264 | ||
265 | static void | |
266 | gcov_write_block (unsigned size) | |
267 | { | |
330d2e2a | 268 | if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1) |
7d63a2fa NS |
269 | gcov_var.error = 1; |
270 | gcov_var.start += size; | |
271 | gcov_var.offset -= size; | |
272 | } | |
273 | ||
ca29da43 NS |
274 | /* Allocate space to write BYTES bytes to the gcov file. Return a |
275 | pointer to those bytes, or NULL on failure. */ | |
276 | ||
160e2e4f | 277 | static gcov_unsigned_t * |
330d2e2a | 278 | gcov_write_words (unsigned words) |
ca29da43 | 279 | { |
160e2e4f | 280 | gcov_unsigned_t *result; |
ca29da43 | 281 | |
e3f0315f | 282 | gcov_nonruntime_assert (gcov_var.mode < 0); |
ca29da43 | 283 | #if IN_LIBGCOV |
7d63a2fa NS |
284 | if (gcov_var.offset >= GCOV_BLOCK_SIZE) |
285 | { | |
286 | gcov_write_block (GCOV_BLOCK_SIZE); | |
287 | if (gcov_var.offset) | |
ca29da43 | 288 | { |
7d63a2fa | 289 | memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4); |
ca29da43 | 290 | } |
7d63a2fa | 291 | } |
ca29da43 | 292 | #else |
330d2e2a NS |
293 | if (gcov_var.offset + words > gcov_var.alloc) |
294 | gcov_allocate (gcov_var.offset + words); | |
ca29da43 | 295 | #endif |
330d2e2a NS |
296 | result = &gcov_var.buffer[gcov_var.offset]; |
297 | gcov_var.offset += words; | |
b8698a0f | 298 | |
ca29da43 NS |
299 | return result; |
300 | } | |
301 | ||
302 | /* Write unsigned VALUE to coverage file. Sets error flag | |
303 | appropriately. */ | |
304 | ||
305 | GCOV_LINKAGE void | |
9b514d25 | 306 | gcov_write_unsigned (gcov_unsigned_t value) |
ca29da43 | 307 | { |
330d2e2a | 308 | gcov_unsigned_t *buffer = gcov_write_words (1); |
ca29da43 | 309 | |
160e2e4f | 310 | buffer[0] = value; |
ca29da43 NS |
311 | } |
312 | ||
313 | /* Write counter VALUE to coverage file. Sets error flag | |
314 | appropriately. */ | |
315 | ||
316 | #if IN_LIBGCOV | |
317 | GCOV_LINKAGE void | |
318 | gcov_write_counter (gcov_type value) | |
319 | { | |
330d2e2a | 320 | gcov_unsigned_t *buffer = gcov_write_words (2); |
ca29da43 | 321 | |
160e2e4f NS |
322 | buffer[0] = (gcov_unsigned_t) value; |
323 | if (sizeof (value) > sizeof (gcov_unsigned_t)) | |
324 | buffer[1] = (gcov_unsigned_t) (value >> 32); | |
325 | else | |
326 | buffer[1] = 0; | |
ca29da43 NS |
327 | } |
328 | #endif /* IN_LIBGCOV */ | |
329 | ||
796621e8 | 330 | #if !IN_LIBGCOV |
ca29da43 NS |
331 | /* Write STRING to coverage file. Sets error flag on file |
332 | error, overflow flag on overflow */ | |
333 | ||
334 | GCOV_LINKAGE void | |
335 | gcov_write_string (const char *string) | |
336 | { | |
337 | unsigned length = 0; | |
160e2e4f NS |
338 | unsigned alloc = 0; |
339 | gcov_unsigned_t *buffer; | |
ca29da43 NS |
340 | |
341 | if (string) | |
342 | { | |
343 | length = strlen (string); | |
160e2e4f | 344 | alloc = (length + 4) >> 2; |
ca29da43 | 345 | } |
b8698a0f | 346 | |
330d2e2a | 347 | buffer = gcov_write_words (1 + alloc); |
7d63a2fa | 348 | |
160e2e4f | 349 | buffer[0] = alloc; |
0040ecb0 ML |
350 | |
351 | if (alloc > 0) | |
352 | { | |
353 | buffer[alloc] = 0; /* place nul terminators. */ | |
354 | memcpy (&buffer[1], string, length); | |
355 | } | |
ca29da43 | 356 | } |
796621e8 | 357 | #endif |
ca29da43 | 358 | |
676519f7 BE |
359 | #if !IN_LIBGCOV |
360 | /* Write FILENAME to coverage file. Sets error flag on file | |
361 | error, overflow flag on overflow */ | |
362 | ||
363 | GCOV_LINKAGE void | |
364 | gcov_write_filename (const char *filename) | |
365 | { | |
366 | if (profile_abs_path_flag && filename && filename[0] | |
367 | && !(IS_DIR_SEPARATOR (filename[0]) | |
368 | #if HAVE_DOS_BASED_FILE_SYSTEM | |
369 | || filename[1] == ':' | |
370 | #endif | |
371 | )) | |
372 | { | |
373 | char *buf = getcwd (NULL, 0); | |
374 | if (buf != NULL && buf[0]) | |
375 | { | |
376 | size_t len = strlen (buf); | |
377 | buf = (char*)xrealloc (buf, len + strlen (filename) + 2); | |
378 | if (!IS_DIR_SEPARATOR (buf[len - 1])) | |
379 | strcat (buf, "/"); | |
380 | strcat (buf, filename); | |
381 | gcov_write_string (buf); | |
382 | free (buf); | |
383 | return; | |
384 | } | |
385 | } | |
386 | ||
387 | gcov_write_string (filename); | |
388 | } | |
389 | #endif | |
390 | ||
474f141e | 391 | #if !IN_LIBGCOV |
ca29da43 NS |
392 | /* Write a tag TAG and reserve space for the record length. Return a |
393 | value to be used for gcov_write_length. */ | |
394 | ||
9b514d25 NS |
395 | GCOV_LINKAGE gcov_position_t |
396 | gcov_write_tag (gcov_unsigned_t tag) | |
ca29da43 | 397 | { |
7d63a2fa | 398 | gcov_position_t result = gcov_var.start + gcov_var.offset; |
330d2e2a | 399 | gcov_unsigned_t *buffer = gcov_write_words (2); |
ca29da43 | 400 | |
160e2e4f NS |
401 | buffer[0] = tag; |
402 | buffer[1] = 0; | |
b8698a0f | 403 | |
ca29da43 NS |
404 | return result; |
405 | } | |
406 | ||
407 | /* Write a record length using POSITION, which was returned by | |
408 | gcov_write_tag. The current file position is the end of the | |
409 | record, and is restored before returning. Returns nonzero on | |
410 | overflow. */ | |
411 | ||
412 | GCOV_LINKAGE void | |
9b514d25 | 413 | gcov_write_length (gcov_position_t position) |
ca29da43 | 414 | { |
7d63a2fa NS |
415 | unsigned offset; |
416 | gcov_unsigned_t length; | |
160e2e4f | 417 | gcov_unsigned_t *buffer; |
7d63a2fa | 418 | |
e3f0315f TJ |
419 | gcov_nonruntime_assert (gcov_var.mode < 0); |
420 | gcov_nonruntime_assert (position + 2 <= gcov_var.start + gcov_var.offset); | |
421 | gcov_nonruntime_assert (position >= gcov_var.start); | |
7d63a2fa | 422 | offset = position - gcov_var.start; |
330d2e2a | 423 | length = gcov_var.offset - offset - 2; |
160e2e4f NS |
424 | buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset]; |
425 | buffer[1] = length; | |
7d63a2fa NS |
426 | if (gcov_var.offset >= GCOV_BLOCK_SIZE) |
427 | gcov_write_block (gcov_var.offset); | |
ca29da43 | 428 | } |
9b514d25 NS |
429 | |
430 | #else /* IN_LIBGCOV */ | |
474f141e NS |
431 | |
432 | /* Write a tag TAG and length LENGTH. */ | |
433 | ||
434 | GCOV_LINKAGE void | |
9b514d25 | 435 | gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length) |
474f141e | 436 | { |
330d2e2a | 437 | gcov_unsigned_t *buffer = gcov_write_words (2); |
474f141e | 438 | |
160e2e4f NS |
439 | buffer[0] = tag; |
440 | buffer[1] = length; | |
474f141e | 441 | } |
ca29da43 | 442 | |
6356f892 | 443 | /* Write a summary structure to the gcov file. Return nonzero on |
ca29da43 NS |
444 | overflow. */ |
445 | ||
446 | GCOV_LINKAGE void | |
9b514d25 | 447 | gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) |
ca29da43 | 448 | { |
512cc015 | 449 | gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH); |
7f3577f5 | 450 | gcov_write_unsigned (summary->runs); |
512cc015 | 451 | gcov_write_unsigned (summary->sum_max); |
ca29da43 | 452 | } |
512cc015 | 453 | |
ca29da43 NS |
454 | #endif /* IN_LIBGCOV */ |
455 | ||
456 | #endif /*!IN_GCOV */ | |
457 | ||
458 | /* Return a pointer to read BYTES bytes from the gcov file. Returns | |
71c0e7fc | 459 | NULL on failure (read past EOF). */ |
ca29da43 | 460 | |
160e2e4f | 461 | static const gcov_unsigned_t * |
330d2e2a | 462 | gcov_read_words (unsigned words) |
ca29da43 | 463 | { |
160e2e4f | 464 | const gcov_unsigned_t *result; |
7d63a2fa | 465 | unsigned excess = gcov_var.length - gcov_var.offset; |
b8698a0f | 466 | |
1bba63a7 AK |
467 | if (gcov_var.mode <= 0) |
468 | return NULL; | |
469 | ||
330d2e2a | 470 | if (excess < words) |
ca29da43 | 471 | { |
7d63a2fa | 472 | gcov_var.start += gcov_var.offset; |
7d63a2fa NS |
473 | if (excess) |
474 | { | |
00a7ba58 | 475 | #if IN_LIBGCOV |
7d63a2fa | 476 | memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4); |
7d63a2fa | 477 | #else |
00a7ba58 JJ |
478 | memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, |
479 | excess * 4); | |
7d63a2fa | 480 | #endif |
00a7ba58 | 481 | } |
7d63a2fa NS |
482 | gcov_var.offset = 0; |
483 | gcov_var.length = excess; | |
484 | #if IN_LIBGCOV | |
7d63a2fa NS |
485 | excess = GCOV_BLOCK_SIZE; |
486 | #else | |
330d2e2a NS |
487 | if (gcov_var.length + words > gcov_var.alloc) |
488 | gcov_allocate (gcov_var.length + words); | |
7d63a2fa NS |
489 | excess = gcov_var.alloc - gcov_var.length; |
490 | #endif | |
dc1f1d7f | 491 | excess = fread (gcov_var.buffer + gcov_var.length, |
330d2e2a | 492 | 1, excess << 2, gcov_var.file) >> 2; |
7d63a2fa | 493 | gcov_var.length += excess; |
330d2e2a | 494 | if (gcov_var.length < words) |
7d63a2fa | 495 | { |
330d2e2a | 496 | gcov_var.overread += words - gcov_var.length; |
7d63a2fa NS |
497 | gcov_var.length = 0; |
498 | return 0; | |
499 | } | |
ca29da43 | 500 | } |
330d2e2a NS |
501 | result = &gcov_var.buffer[gcov_var.offset]; |
502 | gcov_var.offset += words; | |
ca29da43 NS |
503 | return result; |
504 | } | |
505 | ||
506 | /* Read unsigned value from a coverage file. Sets error flag on file | |
507 | error, overflow flag on overflow */ | |
508 | ||
9b514d25 | 509 | GCOV_LINKAGE gcov_unsigned_t |
82a30d6f | 510 | gcov_read_unsigned (void) |
ca29da43 | 511 | { |
160e2e4f | 512 | gcov_unsigned_t value; |
330d2e2a | 513 | const gcov_unsigned_t *buffer = gcov_read_words (1); |
ca29da43 NS |
514 | |
515 | if (!buffer) | |
516 | return 0; | |
160e2e4f | 517 | value = from_file (buffer[0]); |
ca29da43 NS |
518 | return value; |
519 | } | |
520 | ||
521 | /* Read counter value from a coverage file. Sets error flag on file | |
522 | error, overflow flag on overflow */ | |
523 | ||
524 | GCOV_LINKAGE gcov_type | |
82a30d6f | 525 | gcov_read_counter (void) |
ca29da43 | 526 | { |
160e2e4f | 527 | gcov_type value; |
330d2e2a | 528 | const gcov_unsigned_t *buffer = gcov_read_words (2); |
ca29da43 NS |
529 | |
530 | if (!buffer) | |
531 | return 0; | |
160e2e4f NS |
532 | value = from_file (buffer[0]); |
533 | if (sizeof (value) > sizeof (gcov_unsigned_t)) | |
534 | value |= ((gcov_type) from_file (buffer[1])) << 32; | |
535 | else if (buffer[1]) | |
536 | gcov_var.error = -1; | |
6d9901e7 | 537 | |
ca29da43 NS |
538 | return value; |
539 | } | |
540 | ||
1f2bb38a ML |
541 | /* Mangle filename path of BASE and output new allocated pointer with |
542 | mangled path. */ | |
543 | ||
544 | char * | |
545 | mangle_path (char const *base) | |
546 | { | |
547 | /* Convert '/' to '#', convert '..' to '^', | |
548 | convert ':' to '~' on DOS based file system. */ | |
549 | const char *probe; | |
12502bf2 | 550 | char *buffer = (char *)xmalloc (strlen (base) + 1); |
1f2bb38a ML |
551 | char *ptr = buffer; |
552 | ||
553 | #if HAVE_DOS_BASED_FILE_SYSTEM | |
554 | if (base[0] && base[1] == ':') | |
555 | { | |
556 | ptr[0] = base[0]; | |
557 | ptr[1] = '~'; | |
558 | ptr += 2; | |
559 | base += 2; | |
560 | } | |
561 | #endif | |
562 | for (; *base; base = probe) | |
563 | { | |
564 | size_t len; | |
565 | ||
566 | for (probe = base; *probe; probe++) | |
567 | if (*probe == '/') | |
568 | break; | |
569 | len = probe - base; | |
570 | if (len == 2 && base[0] == '.' && base[1] == '.') | |
571 | *ptr++ = '^'; | |
572 | else | |
573 | { | |
574 | memcpy (ptr, base, len); | |
575 | ptr += len; | |
576 | } | |
577 | if (*probe) | |
578 | { | |
579 | *ptr++ = '#'; | |
580 | probe++; | |
581 | } | |
582 | } | |
583 | ||
584 | /* Terminate the string. */ | |
585 | *ptr = '\0'; | |
586 | ||
587 | return buffer; | |
588 | } | |
589 | ||
c77556a5 RX |
590 | /* We need to expose the below function when compiling for gcov-tool. */ |
591 | ||
592 | #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) | |
ca29da43 NS |
593 | /* Read string from coverage file. Returns a pointer to a static |
594 | buffer, or NULL on empty string. You must copy the string before | |
595 | calling another gcov function. */ | |
596 | ||
597 | GCOV_LINKAGE const char * | |
82a30d6f | 598 | gcov_read_string (void) |
ca29da43 NS |
599 | { |
600 | unsigned length = gcov_read_unsigned (); | |
b8698a0f | 601 | |
ca29da43 NS |
602 | if (!length) |
603 | return 0; | |
604 | ||
330d2e2a | 605 | return (const char *) gcov_read_words (length); |
ca29da43 | 606 | } |
796621e8 | 607 | #endif |
ca29da43 NS |
608 | |
609 | GCOV_LINKAGE void | |
610 | gcov_read_summary (struct gcov_summary *summary) | |
611 | { | |
7f3577f5 | 612 | summary->runs = gcov_read_unsigned (); |
512cc015 | 613 | summary->sum_max = gcov_read_unsigned (); |
ca29da43 NS |
614 | } |
615 | ||
c77556a5 RX |
616 | /* We need to expose the below function when compiling for gcov-tool. */ |
617 | ||
618 | #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) | |
7d63a2fa NS |
619 | /* Reset to a known position. BASE should have been obtained from |
620 | gcov_position, LENGTH should be a record length. */ | |
621 | ||
622 | GCOV_LINKAGE void | |
623 | gcov_sync (gcov_position_t base, gcov_unsigned_t length) | |
624 | { | |
e3f0315f | 625 | gcov_nonruntime_assert (gcov_var.mode > 0); |
7d63a2fa NS |
626 | base += length; |
627 | if (base - gcov_var.start <= gcov_var.length) | |
628 | gcov_var.offset = base - gcov_var.start; | |
629 | else | |
630 | { | |
631 | gcov_var.offset = gcov_var.length = 0; | |
330d2e2a NS |
632 | fseek (gcov_var.file, base << 2, SEEK_SET); |
633 | gcov_var.start = ftell (gcov_var.file) >> 2; | |
7d63a2fa NS |
634 | } |
635 | } | |
636 | #endif | |
637 | ||
638 | #if IN_LIBGCOV | |
fa10beec | 639 | /* Move to a given position in a gcov file. */ |
7d63a2fa NS |
640 | |
641 | GCOV_LINKAGE void | |
642 | gcov_seek (gcov_position_t base) | |
643 | { | |
7d63a2fa NS |
644 | if (gcov_var.offset) |
645 | gcov_write_block (gcov_var.offset); | |
00cf2913 | 646 | fseek (gcov_var.file, base << 2, SEEK_SET); |
330d2e2a | 647 | gcov_var.start = ftell (gcov_var.file) >> 2; |
7d63a2fa NS |
648 | } |
649 | #endif | |
650 | ||
ca29da43 NS |
651 | #if IN_GCOV > 0 |
652 | /* Return the modification time of the current gcov file. */ | |
653 | ||
654 | GCOV_LINKAGE time_t | |
82a30d6f | 655 | gcov_time (void) |
ca29da43 NS |
656 | { |
657 | struct stat status; | |
b8698a0f | 658 | |
ca29da43 NS |
659 | if (fstat (fileno (gcov_var.file), &status)) |
660 | return 0; | |
661 | else | |
662 | return status.st_mtime; | |
663 | } | |
664 | #endif /* IN_GCOV */ |