1 | /* File format for coverage information |
2 | Copyright (C) 1996-2017 Free Software Foundation, Inc. |
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 |
10 | Software Foundation; either version 3, or (at your option) any later |
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 | |
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 |
25 | <http://www.gnu.org/licenses/>. */ |
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 | |
30 | #if !IN_GCOV |
31 | static void gcov_write_block (unsigned); |
32 | static gcov_unsigned_t *gcov_write_words (unsigned); |
33 | #endif |
34 | static const gcov_unsigned_t *gcov_read_words (unsigned); |
35 | #if !IN_LIBGCOV |
36 | static void gcov_allocate (unsigned); |
37 | #endif |
38 | |
39 | /* Optimum number of gcov_unsigned_t's read from or written to disk. */ |
40 | #define GCOV_BLOCK_SIZE (1 << 10) |
41 | |
42 | struct gcov_var |
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 */ |
51 | #if IN_LIBGCOV |
52 | /* Holds one block plus 4 bytes, thus all coverage reads & writes |
53 | fit within this buffer and we always can transfer GCOV_BLOCK_SIZE |
54 | to and from the disk. libgcov never backtracks and only writes 4 |
55 | or 8 byte objects. */ |
56 | gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1]; |
57 | #else |
58 | int endian; /* Swap endianness. */ |
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. */ |
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 |
72 | gcov_position (void) |
73 | { |
74 | gcov_nonruntime_assert (gcov_var.mode > 0); |
75 | return gcov_var.start + gcov_var.offset; |
76 | } |
77 | |
78 | /* Return nonzero if the error flag is set. */ |
79 | /* We need to expose this function when compiling for gcov-tool. */ |
80 | #ifndef IN_GCOV_TOOL |
81 | static inline |
82 | #endif |
83 | int |
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 | { |
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 | |
101 | static inline gcov_unsigned_t from_file (gcov_unsigned_t value) |
102 | { |
103 | #if !IN_LIBGCOV |
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 | |
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 |
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 |
118 | creating a new one. If MODE > 0, the file will be opened in |
119 | read-only mode. Otherwise it will be opened for modification. |
120 | Return zero on failure, non-zero on success. */ |
121 | |
122 | GCOV_LINKAGE int |
123 | #if IN_LIBGCOV |
124 | gcov_open (const char *name) |
125 | #else |
126 | gcov_open (const char *name, int mode) |
127 | #endif |
128 | { |
129 | #if IN_LIBGCOV |
130 | int mode = 0; |
131 | #endif |
132 | #if GCOV_LOCKED |
133 | struct flock s_flock; |
134 | int fd; |
135 | |
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 |
141 | |
142 | gcov_nonruntime_assert (!gcov_var.file); |
143 | gcov_var.start = 0; |
144 | gcov_var.offset = gcov_var.length = 0; |
145 | gcov_var.overread = -1u; |
146 | gcov_var.error = 0; |
147 | #if !IN_LIBGCOV |
148 | gcov_var.endian = 0; |
149 | #endif |
150 | #if GCOV_LOCKED |
151 | if (mode > 0) |
152 | { |
153 | /* Read-only mode - acquire a read-lock. */ |
154 | s_flock.l_type = F_RDLCK; |
155 | /* pass mode (ignored) for compatibility */ |
156 | fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR); |
157 | } |
158 | else |
159 | { |
160 | /* Write mode - acquire a write-lock. */ |
161 | s_flock.l_type = F_WRLCK; |
162 | /* Truncate if force new mode. */ |
163 | fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666); |
164 | } |
165 | if (fd < 0) |
166 | return 0; |
167 | |
168 | while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR) |
169 | continue; |
170 | |
171 | gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b" ); |
172 | |
173 | if (!gcov_var.file) |
174 | { |
175 | close (fd); |
176 | return 0; |
177 | } |
178 | #else |
179 | if (mode >= 0) |
180 | /* Open an existing file. */ |
181 | gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b" ); |
182 | |
183 | if (gcov_var.file) |
184 | mode = 1; |
185 | else if (mode <= 0) |
186 | /* Create a new file. */ |
187 | gcov_var.file = fopen (name, "w+b" ); |
188 | |
189 | if (!gcov_var.file) |
190 | return 0; |
191 | #endif |
192 | |
193 | gcov_var.mode = mode ? mode : 1; |
194 | |
195 | setbuf (gcov_var.file, (char *)0); |
196 | |
197 | return 1; |
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 |
204 | gcov_close (void) |
205 | { |
206 | if (gcov_var.file) |
207 | { |
208 | #if !IN_GCOV |
209 | if (gcov_var.offset && gcov_var.mode < 0) |
210 | gcov_write_block (gcov_var.offset); |
211 | #endif |
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 |
221 | gcov_var.mode = 0; |
222 | return gcov_var.error; |
223 | } |
224 | |
225 | #if !IN_LIBGCOV |
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 | |
246 | #if !IN_LIBGCOV |
247 | static void |
248 | gcov_allocate (unsigned length) |
249 | { |
250 | size_t new_size = gcov_var.alloc; |
251 | |
252 | if (!new_size) |
253 | new_size = GCOV_BLOCK_SIZE; |
254 | new_size += length; |
255 | new_size *= 2; |
256 | |
257 | gcov_var.alloc = new_size; |
258 | gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2); |
259 | } |
260 | #endif |
261 | |
262 | #if !IN_GCOV |
263 | /* Write out the current block, if needs be. */ |
264 | |
265 | static void |
266 | gcov_write_block (unsigned size) |
267 | { |
268 | if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1) |
269 | gcov_var.error = 1; |
270 | gcov_var.start += size; |
271 | gcov_var.offset -= size; |
272 | } |
273 | |
274 | /* Allocate space to write BYTES bytes to the gcov file. Return a |
275 | pointer to those bytes, or NULL on failure. */ |
276 | |
277 | static gcov_unsigned_t * |
278 | gcov_write_words (unsigned words) |
279 | { |
280 | gcov_unsigned_t *result; |
281 | |
282 | gcov_nonruntime_assert (gcov_var.mode < 0); |
283 | #if IN_LIBGCOV |
284 | if (gcov_var.offset >= GCOV_BLOCK_SIZE) |
285 | { |
286 | gcov_write_block (GCOV_BLOCK_SIZE); |
287 | if (gcov_var.offset) |
288 | { |
289 | memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4); |
290 | } |
291 | } |
292 | #else |
293 | if (gcov_var.offset + words > gcov_var.alloc) |
294 | gcov_allocate (gcov_var.offset + words); |
295 | #endif |
296 | result = &gcov_var.buffer[gcov_var.offset]; |
297 | gcov_var.offset += words; |
298 | |
299 | return result; |
300 | } |
301 | |
302 | /* Write unsigned VALUE to coverage file. Sets error flag |
303 | appropriately. */ |
304 | |
305 | GCOV_LINKAGE void |
306 | gcov_write_unsigned (gcov_unsigned_t value) |
307 | { |
308 | gcov_unsigned_t *buffer = gcov_write_words (1); |
309 | |
310 | buffer[0] = value; |
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 | { |
320 | gcov_unsigned_t *buffer = gcov_write_words (2); |
321 | |
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; |
327 | } |
328 | #endif /* IN_LIBGCOV */ |
329 | |
330 | #if !IN_LIBGCOV |
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; |
338 | unsigned alloc = 0; |
339 | gcov_unsigned_t *buffer; |
340 | |
341 | if (string) |
342 | { |
343 | length = strlen (string); |
344 | alloc = (length + 4) >> 2; |
345 | } |
346 | |
347 | buffer = gcov_write_words (1 + alloc); |
348 | |
349 | buffer[0] = alloc; |
350 | |
351 | if (alloc > 0) |
352 | { |
353 | buffer[alloc] = 0; /* place nul terminators. */ |
354 | memcpy (&buffer[1], string, length); |
355 | } |
356 | } |
357 | #endif |
358 | |
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 | |
391 | #if !IN_LIBGCOV |
392 | /* Write a tag TAG and reserve space for the record length. Return a |
393 | value to be used for gcov_write_length. */ |
394 | |
395 | GCOV_LINKAGE gcov_position_t |
396 | gcov_write_tag (gcov_unsigned_t tag) |
397 | { |
398 | gcov_position_t result = gcov_var.start + gcov_var.offset; |
399 | gcov_unsigned_t *buffer = gcov_write_words (2); |
400 | |
401 | buffer[0] = tag; |
402 | buffer[1] = 0; |
403 | |
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 |
413 | gcov_write_length (gcov_position_t position) |
414 | { |
415 | unsigned offset; |
416 | gcov_unsigned_t length; |
417 | gcov_unsigned_t *buffer; |
418 | |
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); |
422 | offset = position - gcov_var.start; |
423 | length = gcov_var.offset - offset - 2; |
424 | buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset]; |
425 | buffer[1] = length; |
426 | if (gcov_var.offset >= GCOV_BLOCK_SIZE) |
427 | gcov_write_block (gcov_var.offset); |
428 | } |
429 | |
430 | #else /* IN_LIBGCOV */ |
431 | |
432 | /* Write a tag TAG and length LENGTH. */ |
433 | |
434 | GCOV_LINKAGE void |
435 | gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length) |
436 | { |
437 | gcov_unsigned_t *buffer = gcov_write_words (2); |
438 | |
439 | buffer[0] = tag; |
440 | buffer[1] = length; |
441 | } |
442 | |
443 | /* Write a summary structure to the gcov file. Return nonzero on |
444 | overflow. */ |
445 | |
446 | GCOV_LINKAGE void |
447 | gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) |
448 | { |
449 | unsigned ix, h_ix, bv_ix, h_cnt = 0; |
450 | const struct gcov_ctr_summary *csum; |
451 | unsigned histo_bitvector[GCOV_HISTOGRAM_BITVECTOR_SIZE]; |
452 | |
453 | /* Count number of non-zero histogram entries, and fill in a bit vector |
454 | of non-zero indices. The histogram is only currently computed for arc |
455 | counters. */ |
456 | for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) |
457 | histo_bitvector[bv_ix] = 0; |
458 | csum = &summary->ctrs[GCOV_COUNTER_ARCS]; |
459 | for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++) |
460 | if (csum->histogram[h_ix].num_counters) |
461 | { |
462 | histo_bitvector[h_ix / 32] |= 1 << (h_ix % 32); |
463 | h_cnt++; |
464 | } |
465 | gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH (h_cnt)); |
466 | gcov_write_unsigned (summary->checksum); |
467 | for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) |
468 | { |
469 | gcov_write_unsigned (csum->num); |
470 | gcov_write_unsigned (csum->runs); |
471 | gcov_write_counter (csum->sum_all); |
472 | gcov_write_counter (csum->run_max); |
473 | gcov_write_counter (csum->sum_max); |
474 | if (ix != GCOV_COUNTER_ARCS) |
475 | { |
476 | for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) |
477 | gcov_write_unsigned (0); |
478 | continue; |
479 | } |
480 | for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) |
481 | gcov_write_unsigned (histo_bitvector[bv_ix]); |
482 | for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++) |
483 | { |
484 | if (!csum->histogram[h_ix].num_counters) |
485 | continue; |
486 | gcov_write_unsigned (csum->histogram[h_ix].num_counters); |
487 | gcov_write_counter (csum->histogram[h_ix].min_value); |
488 | gcov_write_counter (csum->histogram[h_ix].cum_value); |
489 | } |
490 | } |
491 | } |
492 | #endif /* IN_LIBGCOV */ |
493 | |
494 | #endif /*!IN_GCOV */ |
495 | |
496 | /* Return a pointer to read BYTES bytes from the gcov file. Returns |
497 | NULL on failure (read past EOF). */ |
498 | |
499 | static const gcov_unsigned_t * |
500 | gcov_read_words (unsigned words) |
501 | { |
502 | const gcov_unsigned_t *result; |
503 | unsigned excess = gcov_var.length - gcov_var.offset; |
504 | |
505 | if (gcov_var.mode <= 0) |
506 | return NULL; |
507 | |
508 | if (excess < words) |
509 | { |
510 | gcov_var.start += gcov_var.offset; |
511 | if (excess) |
512 | { |
513 | #if IN_LIBGCOV |
514 | memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4); |
515 | #else |
516 | memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, |
517 | excess * 4); |
518 | #endif |
519 | } |
520 | gcov_var.offset = 0; |
521 | gcov_var.length = excess; |
522 | #if IN_LIBGCOV |
523 | excess = GCOV_BLOCK_SIZE; |
524 | #else |
525 | if (gcov_var.length + words > gcov_var.alloc) |
526 | gcov_allocate (gcov_var.length + words); |
527 | excess = gcov_var.alloc - gcov_var.length; |
528 | #endif |
529 | excess = fread (gcov_var.buffer + gcov_var.length, |
530 | 1, excess << 2, gcov_var.file) >> 2; |
531 | gcov_var.length += excess; |
532 | if (gcov_var.length < words) |
533 | { |
534 | gcov_var.overread += words - gcov_var.length; |
535 | gcov_var.length = 0; |
536 | return 0; |
537 | } |
538 | } |
539 | result = &gcov_var.buffer[gcov_var.offset]; |
540 | gcov_var.offset += words; |
541 | return result; |
542 | } |
543 | |
544 | /* Read unsigned value from a coverage file. Sets error flag on file |
545 | error, overflow flag on overflow */ |
546 | |
547 | GCOV_LINKAGE gcov_unsigned_t |
548 | gcov_read_unsigned (void) |
549 | { |
550 | gcov_unsigned_t value; |
551 | const gcov_unsigned_t *buffer = gcov_read_words (1); |
552 | |
553 | if (!buffer) |
554 | return 0; |
555 | value = from_file (buffer[0]); |
556 | return value; |
557 | } |
558 | |
559 | /* Read counter value from a coverage file. Sets error flag on file |
560 | error, overflow flag on overflow */ |
561 | |
562 | GCOV_LINKAGE gcov_type |
563 | gcov_read_counter (void) |
564 | { |
565 | gcov_type value; |
566 | const gcov_unsigned_t *buffer = gcov_read_words (2); |
567 | |
568 | if (!buffer) |
569 | return 0; |
570 | value = from_file (buffer[0]); |
571 | if (sizeof (value) > sizeof (gcov_unsigned_t)) |
572 | value |= ((gcov_type) from_file (buffer[1])) << 32; |
573 | else if (buffer[1]) |
574 | gcov_var.error = -1; |
575 | |
576 | return value; |
577 | } |
578 | |
579 | /* We need to expose the below function when compiling for gcov-tool. */ |
580 | |
581 | #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) |
582 | /* Read string from coverage file. Returns a pointer to a static |
583 | buffer, or NULL on empty string. You must copy the string before |
584 | calling another gcov function. */ |
585 | |
586 | GCOV_LINKAGE const char * |
587 | gcov_read_string (void) |
588 | { |
589 | unsigned length = gcov_read_unsigned (); |
590 | |
591 | if (!length) |
592 | return 0; |
593 | |
594 | return (const char *) gcov_read_words (length); |
595 | } |
596 | #endif |
597 | |
598 | GCOV_LINKAGE void |
599 | gcov_read_summary (struct gcov_summary *summary) |
600 | { |
601 | unsigned ix, h_ix, bv_ix, h_cnt = 0; |
602 | struct gcov_ctr_summary *csum; |
603 | unsigned histo_bitvector[GCOV_HISTOGRAM_BITVECTOR_SIZE]; |
604 | unsigned cur_bitvector; |
605 | |
606 | summary->checksum = gcov_read_unsigned (); |
607 | for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) |
608 | { |
609 | csum->num = gcov_read_unsigned (); |
610 | csum->runs = gcov_read_unsigned (); |
611 | csum->sum_all = gcov_read_counter (); |
612 | csum->run_max = gcov_read_counter (); |
613 | csum->sum_max = gcov_read_counter (); |
614 | memset (csum->histogram, 0, |
615 | sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE); |
616 | for (bv_ix = 0; bv_ix < GCOV_HISTOGRAM_BITVECTOR_SIZE; bv_ix++) |
617 | { |
618 | histo_bitvector[bv_ix] = gcov_read_unsigned (); |
619 | #if IN_LIBGCOV |
620 | /* When building libgcov we don't include system.h, which includes |
621 | hwint.h (where popcount_hwi is declared). However, libgcov.a |
622 | is built by the bootstrapped compiler and therefore the builtins |
623 | are always available. */ |
624 | h_cnt += __builtin_popcount (histo_bitvector[bv_ix]); |
625 | #else |
626 | h_cnt += popcount_hwi (histo_bitvector[bv_ix]); |
627 | #endif |
628 | } |
629 | bv_ix = 0; |
630 | h_ix = 0; |
631 | cur_bitvector = 0; |
632 | while (h_cnt--) |
633 | { |
634 | /* Find the index corresponding to the next entry we will read in. |
635 | First find the next non-zero bitvector and re-initialize |
636 | the histogram index accordingly, then right shift and increment |
637 | the index until we find a set bit. */ |
638 | while (!cur_bitvector) |
639 | { |
640 | h_ix = bv_ix * 32; |
641 | if (bv_ix >= GCOV_HISTOGRAM_BITVECTOR_SIZE) |
642 | gcov_error ("corrupted profile info: summary histogram " |
643 | "bitvector is corrupt" ); |
644 | cur_bitvector = histo_bitvector[bv_ix++]; |
645 | } |
646 | while (!(cur_bitvector & 0x1)) |
647 | { |
648 | h_ix++; |
649 | cur_bitvector >>= 1; |
650 | } |
651 | if (h_ix >= GCOV_HISTOGRAM_SIZE) |
652 | gcov_error ("corrupted profile info: summary histogram " |
653 | "index is corrupt" ); |
654 | |
655 | csum->histogram[h_ix].num_counters = gcov_read_unsigned (); |
656 | csum->histogram[h_ix].min_value = gcov_read_counter (); |
657 | csum->histogram[h_ix].cum_value = gcov_read_counter (); |
658 | /* Shift off the index we are done with and increment to the |
659 | corresponding next histogram entry. */ |
660 | cur_bitvector >>= 1; |
661 | h_ix++; |
662 | } |
663 | } |
664 | } |
665 | |
666 | /* We need to expose the below function when compiling for gcov-tool. */ |
667 | |
668 | #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) |
669 | /* Reset to a known position. BASE should have been obtained from |
670 | gcov_position, LENGTH should be a record length. */ |
671 | |
672 | GCOV_LINKAGE void |
673 | gcov_sync (gcov_position_t base, gcov_unsigned_t length) |
674 | { |
675 | gcov_nonruntime_assert (gcov_var.mode > 0); |
676 | base += length; |
677 | if (base - gcov_var.start <= gcov_var.length) |
678 | gcov_var.offset = base - gcov_var.start; |
679 | else |
680 | { |
681 | gcov_var.offset = gcov_var.length = 0; |
682 | fseek (gcov_var.file, base << 2, SEEK_SET); |
683 | gcov_var.start = ftell (gcov_var.file) >> 2; |
684 | } |
685 | } |
686 | #endif |
687 | |
688 | #if IN_LIBGCOV |
689 | /* Move to a given position in a gcov file. */ |
690 | |
691 | GCOV_LINKAGE void |
692 | gcov_seek (gcov_position_t base) |
693 | { |
694 | if (gcov_var.offset) |
695 | gcov_write_block (gcov_var.offset); |
696 | fseek (gcov_var.file, base << 2, SEEK_SET); |
697 | gcov_var.start = ftell (gcov_var.file) >> 2; |
698 | } |
699 | #endif |
700 | |
701 | #if IN_GCOV > 0 |
702 | /* Return the modification time of the current gcov file. */ |
703 | |
704 | GCOV_LINKAGE time_t |
705 | gcov_time (void) |
706 | { |
707 | struct stat status; |
708 | |
709 | if (fstat (fileno (gcov_var.file), &status)) |
710 | return 0; |
711 | else |
712 | return status.st_mtime; |
713 | } |
714 | #endif /* IN_GCOV */ |
715 | |
716 | #if !IN_GCOV |
717 | /* Determine the index into histogram for VALUE. */ |
718 | |
719 | #if IN_LIBGCOV |
720 | static unsigned |
721 | #else |
722 | GCOV_LINKAGE unsigned |
723 | #endif |
724 | gcov_histo_index (gcov_type value) |
725 | { |
726 | gcov_type_unsigned v = (gcov_type_unsigned)value; |
727 | unsigned r = 0; |
728 | unsigned prev2bits = 0; |
729 | |
730 | /* Find index into log2 scale histogram, where each of the log2 |
731 | sized buckets is divided into 4 linear sub-buckets for better |
732 | focus in the higher buckets. */ |
733 | |
734 | /* Find the place of the most-significant bit set. */ |
735 | if (v > 0) |
736 | { |
737 | #if IN_LIBGCOV |
738 | /* When building libgcov we don't include system.h, which includes |
739 | hwint.h (where floor_log2 is declared). However, libgcov.a |
740 | is built by the bootstrapped compiler and therefore the builtins |
741 | are always available. */ |
742 | r = sizeof (long long) * __CHAR_BIT__ - 1 - __builtin_clzll (v); |
743 | #else |
744 | /* We use floor_log2 from hwint.c, which takes a HOST_WIDE_INT |
745 | that is 64 bits and gcov_type_unsigned is 64 bits. */ |
746 | r = floor_log2 (v); |
747 | #endif |
748 | } |
749 | |
750 | /* If at most the 2 least significant bits are set (value is |
751 | 0 - 3) then that value is our index into the lowest set of |
752 | four buckets. */ |
753 | if (r < 2) |
754 | return (unsigned)value; |
755 | |
756 | gcov_nonruntime_assert (r < 64); |
757 | |
758 | /* Find the two next most significant bits to determine which |
759 | of the four linear sub-buckets to select. */ |
760 | prev2bits = (v >> (r - 2)) & 0x3; |
761 | /* Finally, compose the final bucket index from the log2 index and |
762 | the next 2 bits. The minimum r value at this point is 2 since we |
763 | returned above if r was 2 or more, so the minimum bucket at this |
764 | point is 4. */ |
765 | return (r - 1) * 4 + prev2bits; |
766 | } |
767 | |
768 | /* Merge SRC_HISTO into TGT_HISTO. The counters are assumed to be in |
769 | the same relative order in both histograms, and are matched up |
770 | and merged in reverse order. Each counter is assigned an equal portion of |
771 | its entry's original cumulative counter value when computing the |
772 | new merged cum_value. */ |
773 | |
774 | static void gcov_histogram_merge (gcov_bucket_type *tgt_histo, |
775 | gcov_bucket_type *src_histo) |
776 | { |
777 | int src_i, tgt_i, tmp_i = 0; |
778 | unsigned src_num, tgt_num, merge_num; |
779 | gcov_type src_cum, tgt_cum, merge_src_cum, merge_tgt_cum, merge_cum; |
780 | gcov_type merge_min; |
781 | gcov_bucket_type tmp_histo[GCOV_HISTOGRAM_SIZE]; |
782 | int src_done = 0; |
783 | |
784 | memset (tmp_histo, 0, sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE); |
785 | |
786 | /* Assume that the counters are in the same relative order in both |
787 | histograms. Walk the histograms from largest to smallest entry, |
788 | matching up and combining counters in order. */ |
789 | src_num = 0; |
790 | src_cum = 0; |
791 | src_i = GCOV_HISTOGRAM_SIZE - 1; |
792 | for (tgt_i = GCOV_HISTOGRAM_SIZE - 1; tgt_i >= 0 && !src_done; tgt_i--) |
793 | { |
794 | tgt_num = tgt_histo[tgt_i].num_counters; |
795 | tgt_cum = tgt_histo[tgt_i].cum_value; |
796 | /* Keep going until all of the target histogram's counters at this |
797 | position have been matched and merged with counters from the |
798 | source histogram. */ |
799 | while (tgt_num > 0 && !src_done) |
800 | { |
801 | /* If this is either the first time through this loop or we just |
802 | exhausted the previous non-zero source histogram entry, look |
803 | for the next non-zero source histogram entry. */ |
804 | if (!src_num) |
805 | { |
806 | /* Locate the next non-zero entry. */ |
807 | while (src_i >= 0 && !src_histo[src_i].num_counters) |
808 | src_i--; |
809 | /* If source histogram has fewer counters, then just copy over the |
810 | remaining target counters and quit. */ |
811 | if (src_i < 0) |
812 | { |
813 | tmp_histo[tgt_i].num_counters += tgt_num; |
814 | tmp_histo[tgt_i].cum_value += tgt_cum; |
815 | if (!tmp_histo[tgt_i].min_value || |
816 | tgt_histo[tgt_i].min_value < tmp_histo[tgt_i].min_value) |
817 | tmp_histo[tgt_i].min_value = tgt_histo[tgt_i].min_value; |
818 | while (--tgt_i >= 0) |
819 | { |
820 | tmp_histo[tgt_i].num_counters |
821 | += tgt_histo[tgt_i].num_counters; |
822 | tmp_histo[tgt_i].cum_value += tgt_histo[tgt_i].cum_value; |
823 | if (!tmp_histo[tgt_i].min_value || |
824 | tgt_histo[tgt_i].min_value |
825 | < tmp_histo[tgt_i].min_value) |
826 | tmp_histo[tgt_i].min_value = tgt_histo[tgt_i].min_value; |
827 | } |
828 | |
829 | src_done = 1; |
830 | break; |
831 | } |
832 | |
833 | src_num = src_histo[src_i].num_counters; |
834 | src_cum = src_histo[src_i].cum_value; |
835 | } |
836 | |
837 | /* The number of counters to merge on this pass is the minimum |
838 | of the remaining counters from the current target and source |
839 | histogram entries. */ |
840 | merge_num = tgt_num; |
841 | if (src_num < merge_num) |
842 | merge_num = src_num; |
843 | |
844 | /* The merged min_value is the sum of the min_values from target |
845 | and source. */ |
846 | merge_min = tgt_histo[tgt_i].min_value + src_histo[src_i].min_value; |
847 | |
848 | /* Compute the portion of source and target entries' cum_value |
849 | that will be apportioned to the counters being merged. |
850 | The total remaining cum_value from each entry is divided |
851 | equally among the counters from that histogram entry if we |
852 | are not merging all of them. */ |
853 | merge_src_cum = src_cum; |
854 | if (merge_num < src_num) |
855 | merge_src_cum = merge_num * src_cum / src_num; |
856 | merge_tgt_cum = tgt_cum; |
857 | if (merge_num < tgt_num) |
858 | merge_tgt_cum = merge_num * tgt_cum / tgt_num; |
859 | /* The merged cum_value is the sum of the source and target |
860 | components. */ |
861 | merge_cum = merge_src_cum + merge_tgt_cum; |
862 | |
863 | /* Update the remaining number of counters and cum_value left |
864 | to be merged from this source and target entry. */ |
865 | src_cum -= merge_src_cum; |
866 | tgt_cum -= merge_tgt_cum; |
867 | src_num -= merge_num; |
868 | tgt_num -= merge_num; |
869 | |
870 | /* The merged counters get placed in the new merged histogram |
871 | at the entry for the merged min_value. */ |
872 | tmp_i = gcov_histo_index (merge_min); |
873 | gcov_nonruntime_assert (tmp_i < GCOV_HISTOGRAM_SIZE); |
874 | tmp_histo[tmp_i].num_counters += merge_num; |
875 | tmp_histo[tmp_i].cum_value += merge_cum; |
876 | if (!tmp_histo[tmp_i].min_value || |
877 | merge_min < tmp_histo[tmp_i].min_value) |
878 | tmp_histo[tmp_i].min_value = merge_min; |
879 | |
880 | /* Ensure the search for the next non-zero src_histo entry starts |
881 | at the next smallest histogram bucket. */ |
882 | if (!src_num) |
883 | src_i--; |
884 | } |
885 | } |
886 | |
887 | gcov_nonruntime_assert (tgt_i < 0); |
888 | |
889 | /* In the case where there were more counters in the source histogram, |
890 | accumulate the remaining unmerged cumulative counter values. Add |
891 | those to the smallest non-zero target histogram entry. Otherwise, |
892 | the total cumulative counter values in the histogram will be smaller |
893 | than the sum_all stored in the summary, which will complicate |
894 | computing the working set information from the histogram later on. */ |
895 | if (src_num) |
896 | src_i--; |
897 | while (src_i >= 0) |
898 | { |
899 | src_cum += src_histo[src_i].cum_value; |
900 | src_i--; |
901 | } |
902 | /* At this point, tmp_i should be the smallest non-zero entry in the |
903 | tmp_histo. */ |
904 | gcov_nonruntime_assert (tmp_i >= 0 && tmp_i < GCOV_HISTOGRAM_SIZE |
905 | && tmp_histo[tmp_i].num_counters > 0); |
906 | tmp_histo[tmp_i].cum_value += src_cum; |
907 | |
908 | /* Finally, copy the merged histogram into tgt_histo. */ |
909 | memcpy (tgt_histo, tmp_histo, |
910 | sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE); |
911 | } |
912 | #endif /* !IN_GCOV */ |
913 | |
914 | /* This is used by gcov-dump (IN_GCOV == -1) and in the compiler |
915 | (!IN_GCOV && !IN_LIBGCOV). */ |
916 | #if IN_GCOV <= 0 && !IN_LIBGCOV |
917 | /* Compute the working set information from the counter histogram in |
918 | the profile summary. This is an array of information corresponding to a |
919 | range of percentages of the total execution count (sum_all), and includes |
920 | the number of counters required to cover that working set percentage and |
921 | the minimum counter value in that working set. */ |
922 | |
923 | GCOV_LINKAGE void |
924 | compute_working_sets (const struct gcov_ctr_summary *summary, |
925 | gcov_working_set_t *gcov_working_sets) |
926 | { |
927 | gcov_type working_set_cum_values[NUM_GCOV_WORKING_SETS]; |
928 | gcov_type ws_cum_hotness_incr; |
929 | gcov_type cum, tmp_cum; |
930 | const gcov_bucket_type *histo_bucket; |
931 | unsigned ws_ix, c_num, count; |
932 | int h_ix; |
933 | |
934 | /* Compute the amount of sum_all that the cumulative hotness grows |
935 | by in each successive working set entry, which depends on the |
936 | number of working set entries. */ |
937 | ws_cum_hotness_incr = summary->sum_all / NUM_GCOV_WORKING_SETS; |
938 | |
939 | /* Next fill in an array of the cumulative hotness values corresponding |
940 | to each working set summary entry we are going to compute below. |
941 | Skip 0% statistics, which can be extrapolated from the |
942 | rest of the summary data. */ |
943 | cum = ws_cum_hotness_incr; |
944 | for (ws_ix = 0; ws_ix < NUM_GCOV_WORKING_SETS; |
945 | ws_ix++, cum += ws_cum_hotness_incr) |
946 | working_set_cum_values[ws_ix] = cum; |
947 | /* The last summary entry is reserved for (roughly) 99.9% of the |
948 | working set. Divide by 1024 so it becomes a shift, which gives |
949 | almost exactly 99.9%. */ |
950 | working_set_cum_values[NUM_GCOV_WORKING_SETS-1] |
951 | = summary->sum_all - summary->sum_all/1024; |
952 | |
953 | /* Next, walk through the histogram in decending order of hotness |
954 | and compute the statistics for the working set summary array. |
955 | As histogram entries are accumulated, we check to see which |
956 | working set entries have had their expected cum_value reached |
957 | and fill them in, walking the working set entries in increasing |
958 | size of cum_value. */ |
959 | ws_ix = 0; /* The current entry into the working set array. */ |
960 | cum = 0; /* The current accumulated counter sum. */ |
961 | count = 0; /* The current accumulated count of block counters. */ |
962 | for (h_ix = GCOV_HISTOGRAM_SIZE - 1; |
963 | h_ix >= 0 && ws_ix < NUM_GCOV_WORKING_SETS; h_ix--) |
964 | { |
965 | histo_bucket = &summary->histogram[h_ix]; |
966 | |
967 | /* If we haven't reached the required cumulative counter value for |
968 | the current working set percentage, simply accumulate this histogram |
969 | entry into the running sums and continue to the next histogram |
970 | entry. */ |
971 | if (cum + histo_bucket->cum_value < working_set_cum_values[ws_ix]) |
972 | { |
973 | cum += histo_bucket->cum_value; |
974 | count += histo_bucket->num_counters; |
975 | continue; |
976 | } |
977 | |
978 | /* If adding the current histogram entry's cumulative counter value |
979 | causes us to exceed the current working set size, then estimate |
980 | how many of this histogram entry's counter values are required to |
981 | reach the working set size, and fill in working set entries |
982 | as we reach their expected cumulative value. */ |
983 | for (c_num = 0, tmp_cum = cum; |
984 | c_num < histo_bucket->num_counters && ws_ix < NUM_GCOV_WORKING_SETS; |
985 | c_num++) |
986 | { |
987 | count++; |
988 | /* If we haven't reached the last histogram entry counter, add |
989 | in the minimum value again. This will underestimate the |
990 | cumulative sum so far, because many of the counter values in this |
991 | entry may have been larger than the minimum. We could add in the |
992 | average value every time, but that would require an expensive |
993 | divide operation. */ |
994 | if (c_num + 1 < histo_bucket->num_counters) |
995 | tmp_cum += histo_bucket->min_value; |
996 | /* If we have reached the last histogram entry counter, then add |
997 | in the entire cumulative value. */ |
998 | else |
999 | tmp_cum = cum + histo_bucket->cum_value; |
1000 | |
1001 | /* Next walk through successive working set entries and fill in |
1002 | the statistics for any whose size we have reached by accumulating |
1003 | this histogram counter. */ |
1004 | while (ws_ix < NUM_GCOV_WORKING_SETS |
1005 | && tmp_cum >= working_set_cum_values[ws_ix]) |
1006 | { |
1007 | gcov_working_sets[ws_ix].num_counters = count; |
1008 | gcov_working_sets[ws_ix].min_counter |
1009 | = histo_bucket->min_value; |
1010 | ws_ix++; |
1011 | } |
1012 | } |
1013 | /* Finally, update the running cumulative value since we were |
1014 | using a temporary above. */ |
1015 | cum += histo_bucket->cum_value; |
1016 | } |
1017 | gcov_nonruntime_assert (ws_ix == NUM_GCOV_WORKING_SETS); |
1018 | } |
1019 | #endif /* IN_GCOV <= 0 && !IN_LIBGCOV */ |
1020 | |