File: | build/gcc/../libgcc/libgcov-driver-system.c |
Warning: | line 202, column 4 Argument to free() is offset by 1 byte from the start of memory allocated by malloc() |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* Routines required for instrumenting a program. */ | |||
2 | /* Compile this one with gcc. */ | |||
3 | /* Copyright (C) 1989-2023 Free Software Foundation, Inc. | |||
4 | ||||
5 | This file is part of GCC. | |||
6 | ||||
7 | GCC is free software; you can redistribute it and/or modify it under | |||
8 | the terms of the GNU General Public License as published by the Free | |||
9 | Software Foundation; either version 3, or (at your option) any later | |||
10 | version. | |||
11 | ||||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |||
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |||
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |||
15 | for more details. | |||
16 | ||||
17 | Under Section 7 of GPL version 3, you are granted additional | |||
18 | permissions described in the GCC Runtime Library Exception, version | |||
19 | 3.1, as published by the Free Software Foundation. | |||
20 | ||||
21 | You should have received a copy of the GNU General Public License and | |||
22 | a copy of the GCC Runtime Library Exception along with this program; | |||
23 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |||
24 | <http://www.gnu.org/licenses/>. */ | |||
25 | ||||
26 | #include "libgcov.h" | |||
27 | #include "gcov-io.h" | |||
28 | ||||
29 | /* Return 1, if all counter values are zero, otherwise 0. */ | |||
30 | ||||
31 | static inline int | |||
32 | are_all_counters_zero (const struct gcov_ctr_info *ci_ptr) | |||
33 | { | |||
34 | for (unsigned i = 0; i < ci_ptr->num; i++) | |||
35 | if (ci_ptr->values[i] != 0) | |||
36 | return 0; | |||
37 | ||||
38 | return 1; | |||
39 | } | |||
40 | ||||
41 | #if defined(inhibit_libc) | |||
42 | /* If libc and its header files are not available, provide dummy functions. */ | |||
43 | ||||
44 | #if defined(L_gcov1) | |||
45 | void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {} | |||
46 | #endif | |||
47 | ||||
48 | #else /* inhibit_libc */ | |||
49 | ||||
50 | #if GCOV_LOCKED1 | |||
51 | #include <fcntl.h> | |||
52 | #include <errno(*__errno_location ()).h> | |||
53 | #include <sys/stat.h> | |||
54 | #elif GCOV_LOCKED_WITH_LOCKING0 | |||
55 | #include <fcntl.h> | |||
56 | #include <sys/locking.h> | |||
57 | #include <sys/stat.h> | |||
58 | #endif | |||
59 | ||||
60 | #if HAVE_SYS_MMAN_H1 | |||
61 | #include <sys/mman.h> | |||
62 | #endif | |||
63 | ||||
64 | #endif /* inhibit_libc */ | |||
65 | ||||
66 | #if defined(L_gcov1) && !defined(inhibit_libc) | |||
67 | #define NEED_L_GCOV | |||
68 | #endif | |||
69 | ||||
70 | #if defined(L_gcov_info_to_gcda) && !IN_GCOV_TOOL1 | |||
71 | #define NEED_L_GCOV_INFO_TO_GCDA | |||
72 | #endif | |||
73 | ||||
74 | #ifdef NEED_L_GCOV | |||
75 | /* A utility function for outputting errors. */ | |||
76 | static int gcov_error (const char *, ...); | |||
77 | ||||
78 | #if !IN_GCOV_TOOL1 | |||
79 | static void gcov_error_exit (void); | |||
80 | #endif | |||
81 | ||||
82 | #include "gcov-io.cc" | |||
83 | ||||
84 | #define GCOV_PROF_PREFIX"libgcov profiling error:%s:" "libgcov profiling error:%s:" | |||
85 | ||||
86 | struct gcov_fn_buffer | |||
87 | { | |||
88 | struct gcov_fn_buffer *next; | |||
89 | unsigned fn_ix; | |||
90 | struct gcov_fn_info info; | |||
91 | /* note gcov_fn_info ends in a trailing array. */ | |||
92 | }; | |||
93 | ||||
94 | struct gcov_summary_buffer | |||
95 | { | |||
96 | struct gcov_summary_buffer *next; | |||
97 | struct gcov_summary summary; | |||
98 | }; | |||
99 | ||||
100 | /* A struct that bundles all the related information about the | |||
101 | gcda filename. */ | |||
102 | ||||
103 | struct gcov_filename | |||
104 | { | |||
105 | char *filename; /* filename buffer */ | |||
106 | int strip; /* leading chars to strip from filename */ | |||
107 | char *prefix; /* prefix string */ | |||
108 | }; | |||
109 | ||||
110 | static struct gcov_fn_buffer * | |||
111 | free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer, | |||
112 | unsigned limit) | |||
113 | { | |||
114 | struct gcov_fn_buffer *next; | |||
115 | unsigned ix, n_ctr = 0; | |||
116 | ||||
117 | if (!buffer) | |||
118 | return 0; | |||
119 | next = buffer->next; | |||
120 | ||||
121 | for (ix = 0; ix != limit; ix++) | |||
122 | if (gi_ptr->merge[ix]) | |||
123 | free (buffer->info.ctrs[n_ctr++].values); | |||
124 | free (buffer); | |||
125 | return next; | |||
126 | } | |||
127 | ||||
128 | static struct gcov_fn_buffer ** | |||
129 | buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr, | |||
130 | struct gcov_fn_buffer **end_ptr, unsigned fn_ix) | |||
131 | { | |||
132 | unsigned n_ctrs = 0, ix = 0; | |||
133 | struct gcov_fn_buffer *fn_buffer; | |||
134 | unsigned len; | |||
135 | ||||
136 | for (ix = GCOV_COUNTERS; ix--;) | |||
137 | if (gi_ptr->merge[ix]) | |||
138 | n_ctrs++; | |||
139 | ||||
140 | len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs; | |||
141 | fn_buffer = (struct gcov_fn_buffer *) xmallocmalloc (len); | |||
142 | ||||
143 | if (!fn_buffer) | |||
144 | goto fail; | |||
145 | ||||
146 | fn_buffer->next = 0; | |||
147 | fn_buffer->fn_ix = fn_ix; | |||
148 | fn_buffer->info.ident = gcov_read_unsigned (); | |||
149 | fn_buffer->info.lineno_checksum = gcov_read_unsigned (); | |||
150 | fn_buffer->info.cfg_checksum = gcov_read_unsigned (); | |||
151 | ||||
152 | for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++) | |||
153 | { | |||
154 | gcov_unsigned_t length; | |||
155 | gcov_type *values; | |||
156 | ||||
157 | if (!gi_ptr->merge[ix]) | |||
158 | continue; | |||
159 | ||||
160 | if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix)(((gcov_unsigned_t)0x01a10000) + ((gcov_unsigned_t)(ix) << 17))) | |||
161 | { | |||
162 | len = 0; | |||
163 | goto fail; | |||
164 | } | |||
165 | ||||
166 | length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ())((gcov_read_unsigned () / 4) / 2); | |||
167 | len = length * sizeof (gcov_type); | |||
168 | values = (gcov_type *) xmallocmalloc (len); | |||
169 | if (!values) | |||
170 | goto fail; | |||
171 | ||||
172 | fn_buffer->info.ctrs[n_ctrs].num = length; | |||
173 | fn_buffer->info.ctrs[n_ctrs].values = values; | |||
174 | ||||
175 | while (length--) | |||
176 | *values++ = gcov_read_counter (); | |||
177 | n_ctrs++; | |||
178 | } | |||
179 | ||||
180 | *end_ptr = fn_buffer; | |||
181 | return &fn_buffer->next; | |||
182 | ||||
183 | fail: | |||
184 | gcov_error (GCOV_PROF_PREFIX"libgcov profiling error:%s:" "Function %u %s %u \n", filename, fn_ix, | |||
185 | len ? "cannot allocate" : "counter mismatch", len ? len : ix); | |||
186 | ||||
187 | return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix); | |||
188 | } | |||
189 | ||||
190 | /* Convert VERSION into a string description and return the it. | |||
191 | BUFFER is used for storage of the string. The code should be | |||
192 | aligned wit gcov-iov.c. */ | |||
193 | ||||
194 | static char * | |||
195 | gcov_version_string (char *buffer, char version[4]) | |||
196 | { | |||
197 | if (version[0] < 'A' || version[0] > 'Z' | |||
198 | || version[1] < '0' || version[1] > '9' | |||
199 | || version[2] < '0' || version[2] > '9') | |||
200 | sprintf (buffer, "(unknown)"); | |||
201 | else | |||
202 | { | |||
203 | unsigned major = 10 * (version[0] - 'A') + (version[1] - '0'); | |||
204 | unsigned minor = version[2] - '0'; | |||
205 | sprintf (buffer, "%u.%u (%s)", major, minor, | |||
206 | version[3] == '*' ? "release" : "experimental"); | |||
207 | } | |||
208 | return buffer; | |||
209 | } | |||
210 | ||||
211 | /* Check if VERSION of the info block PTR matches libgcov one. | |||
212 | Return 1 on success, or zero in case of versions mismatch. | |||
213 | If FILENAME is not NULL, its value used for reporting purposes | |||
214 | instead of value from the info block. */ | |||
215 | ||||
216 | static int | |||
217 | gcov_version (struct gcov_info *ptr, gcov_unsigned_t version, | |||
218 | const char *filename) | |||
219 | { | |||
220 | if (version != GCOV_VERSION((gcov_unsigned_t)0x42333020)) | |||
221 | { | |||
222 | char v[4], e[4]; | |||
223 | char ver_string[128], expected_string[128]; | |||
224 | ||||
225 | GCOV_UNSIGNED2STRING (v, version)((v)[0] = (char)((version) >> 24), (v)[1] = (char)((version ) >> 16), (v)[2] = (char)((version) >> 8), (v)[3] = (char)((version) >> 0)); | |||
226 | GCOV_UNSIGNED2STRING (e, GCOV_VERSION)((e)[0] = (char)((((gcov_unsigned_t)0x42333020)) >> 24) , (e)[1] = (char)((((gcov_unsigned_t)0x42333020)) >> 16 ), (e)[2] = (char)((((gcov_unsigned_t)0x42333020)) >> 8 ), (e)[3] = (char)((((gcov_unsigned_t)0x42333020)) >> 0 )); | |||
227 | ||||
228 | gcov_error (GCOV_PROF_PREFIX"libgcov profiling error:%s:" "Version mismatch - expected %s (%.4s) " | |||
229 | "got %s (%.4s)\n", | |||
230 | filename? filename : ptr->filename, | |||
231 | gcov_version_string (expected_string, e), e, | |||
232 | gcov_version_string (ver_string, v), v); | |||
233 | return 0; | |||
234 | } | |||
235 | return 1; | |||
236 | } | |||
237 | ||||
238 | /* buffer for the fn_data from another program. */ | |||
239 | static struct gcov_fn_buffer *fn_buffer; | |||
240 | ||||
241 | /* Including system dependent components. */ | |||
242 | #include "libgcov-driver-system.c" | |||
243 | ||||
244 | /* This function merges counters in GI_PTR to an existing gcda file. | |||
245 | Return 0 on success. | |||
246 | Return -1 on error. In this case, caller will goto read_fatal. */ | |||
247 | ||||
248 | static int | |||
249 | merge_one_data (const char *filename, | |||
250 | struct gcov_info *gi_ptr, | |||
251 | struct gcov_summary *summary) | |||
252 | { | |||
253 | gcov_unsigned_t tag, length; | |||
254 | unsigned t_ix; | |||
255 | int f_ix = -1; | |||
256 | int error = 0; | |||
257 | struct gcov_fn_buffer **fn_tail = &fn_buffer; | |||
258 | ||||
259 | length = gcov_read_unsigned (); | |||
260 | if (!gcov_version (gi_ptr, length, filename)) | |||
261 | return -1; | |||
262 | ||||
263 | /* Skip timestamp. */ | |||
264 | gcov_read_unsigned (); | |||
265 | ||||
266 | length = gcov_read_unsigned (); | |||
267 | if (length != gi_ptr->checksum) | |||
268 | { | |||
269 | /* Read from a different compilation. Overwrite the file. */ | |||
270 | gcov_error (GCOV_PROF_PREFIX"libgcov profiling error:%s:" "overwriting an existing profile data " | |||
271 | "with a different checksum\n", filename); | |||
272 | return 0; | |||
273 | } | |||
274 | ||||
275 | tag = gcov_read_unsigned (); | |||
276 | if (tag != GCOV_TAG_OBJECT_SUMMARY((gcov_unsigned_t)0xa1000000)) | |||
277 | goto read_mismatch; | |||
278 | length = gcov_read_unsigned (); | |||
279 | gcc_assert (length > 0)((void)(!(length > 0) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libgcc/libgcov-driver.c" , 279, __FUNCTION__), 0 : 0)); | |||
280 | gcov_read_summary (summary); | |||
281 | ||||
282 | tag = gcov_read_unsigned (); | |||
283 | /* Merge execution counts for each function. */ | |||
284 | for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; | |||
285 | f_ix++, tag = gcov_read_unsigned ()) | |||
286 | { | |||
287 | const struct gcov_ctr_info *ci_ptr; | |||
288 | const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; | |||
289 | ||||
290 | if (tag != GCOV_TAG_FUNCTION((gcov_unsigned_t)0x01000000)) | |||
291 | goto read_mismatch; | |||
292 | ||||
293 | length = gcov_read_unsigned (); | |||
294 | if (!length) | |||
295 | /* This function did not appear in the other program. | |||
296 | We have nothing to merge. */ | |||
297 | continue; | |||
298 | ||||
299 | if (length != GCOV_TAG_FUNCTION_LENGTH(3 * 4)) | |||
300 | goto read_mismatch; | |||
301 | ||||
302 | if (!gfi_ptr || gfi_ptr->key != gi_ptr) | |||
303 | { | |||
304 | /* This function appears in the other program. We | |||
305 | need to buffer the information in order to write | |||
306 | it back out -- we'll be inserting data before | |||
307 | this point, so cannot simply keep the data in the | |||
308 | file. */ | |||
309 | fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix); | |||
310 | if (!fn_tail) | |||
311 | goto read_mismatch; | |||
312 | continue; | |||
313 | } | |||
314 | ||||
315 | length = gcov_read_unsigned (); | |||
316 | if (length != gfi_ptr->ident) | |||
317 | goto read_mismatch; | |||
318 | ||||
319 | length = gcov_read_unsigned (); | |||
320 | if (length != gfi_ptr->lineno_checksum) | |||
321 | goto read_mismatch; | |||
322 | ||||
323 | length = gcov_read_unsigned (); | |||
324 | if (length != gfi_ptr->cfg_checksum) | |||
325 | goto read_mismatch; | |||
326 | ||||
327 | ci_ptr = gfi_ptr->ctrs; | |||
328 | for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) | |||
329 | { | |||
330 | gcov_merge_fn merge = gi_ptr->merge[t_ix]; | |||
331 | ||||
332 | if (!merge) | |||
333 | continue; | |||
334 | ||||
335 | tag = gcov_read_unsigned (); | |||
336 | int read_length = (int)gcov_read_unsigned (); | |||
337 | length = abs (read_length); | |||
338 | if (tag != GCOV_TAG_FOR_COUNTER (t_ix)(((gcov_unsigned_t)0x01a10000) + ((gcov_unsigned_t)(t_ix) << 17)) | |||
339 | || (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)((ci_ptr->num) * 2 * 4) | |||
340 | && t_ix != GCOV_COUNTER_V_TOPN | |||
341 | && t_ix != GCOV_COUNTER_V_INDIR)) | |||
342 | goto read_mismatch; | |||
343 | /* Merging with all zero counters does not make sense. */ | |||
344 | if (read_length > 0) | |||
345 | (*merge) (ci_ptr->values, ci_ptr->num); | |||
346 | ci_ptr++; | |||
347 | } | |||
348 | if ((error = gcov_is_error ())) | |||
349 | goto read_error; | |||
350 | } | |||
351 | ||||
352 | if (tag) | |||
353 | { | |||
354 | read_mismatch:; | |||
355 | gcov_error (GCOV_PROF_PREFIX"libgcov profiling error:%s:" "Merge mismatch for %s %u\n", | |||
356 | filename, f_ix >= 0 ? "function" : "summary", | |||
357 | f_ix < 0 ? -1 - f_ix : f_ix); | |||
358 | return -1; | |||
359 | } | |||
360 | return 0; | |||
361 | ||||
362 | read_error: | |||
363 | gcov_error (GCOV_PROF_PREFIX"libgcov profiling error:%s:" "%s merging\n", filename, | |||
364 | error < 0 ? "Overflow": "Error"); | |||
365 | return -1; | |||
366 | } | |||
367 | ||||
368 | /* Write the DATA of LENGTH characters to the gcov file. */ | |||
369 | ||||
370 | static void | |||
371 | gcov_dump_handler (const void *data, | |||
372 | unsigned length, | |||
373 | void *arg ATTRIBUTE_UNUSED__attribute__ ((__unused__))) | |||
374 | { | |||
375 | gcov_write (data, length); | |||
376 | } | |||
377 | ||||
378 | /* Allocate SIZE characters and return the address of the allocated memory. */ | |||
379 | ||||
380 | static void * | |||
381 | gcov_allocate_handler (unsigned size, void *arg ATTRIBUTE_UNUSED__attribute__ ((__unused__))) | |||
382 | { | |||
383 | return xmallocmalloc (size); | |||
384 | } | |||
385 | #endif /* NEED_L_GCOV */ | |||
386 | ||||
387 | #if defined(NEED_L_GCOV) || defined(NEED_L_GCOV_INFO_TO_GCDA) | |||
388 | /* Dump the WORD using the DUMP handler called with ARG. */ | |||
389 | ||||
390 | static inline void | |||
391 | dump_unsigned (gcov_unsigned_t word, | |||
392 | void (*dump_fn) (const void *, unsigned, void *), | |||
393 | void *arg) | |||
394 | { | |||
395 | (*dump_fn) (&word, sizeof (word), arg); | |||
396 | } | |||
397 | ||||
398 | /* Dump the COUNTER using the DUMP handler called with ARG. */ | |||
399 | ||||
400 | static inline void | |||
401 | dump_counter (gcov_type counter, | |||
402 | void (*dump_fn) (const void *, unsigned, void *), | |||
403 | void *arg) | |||
404 | { | |||
405 | dump_unsigned ((gcov_unsigned_t)counter, dump_fn, arg); | |||
406 | ||||
407 | if (sizeof (counter) > sizeof (gcov_unsigned_t)) | |||
408 | dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump_fn, arg); | |||
409 | else | |||
410 | dump_unsigned (0, dump_fn, arg); | |||
411 | } | |||
412 | ||||
413 | /* Dump the STRING using the DUMP handler called with ARG. */ | |||
414 | ||||
415 | static inline void | |||
416 | ATTRIBUTE_UNUSED__attribute__ ((__unused__)) | |||
417 | dump_string (const char *string, | |||
418 | void (*dump_fn) (const void *, unsigned, void *), | |||
419 | void *arg) | |||
420 | { | |||
421 | unsigned length = 0; | |||
422 | ||||
423 | if (string) | |||
424 | length = strlen (string) + 1; | |||
425 | ||||
426 | dump_unsigned (length, dump_fn, arg); | |||
427 | if (string) | |||
428 | (*dump_fn) (string, length, arg); | |||
429 | } | |||
430 | ||||
431 | #define MAX(X,Y)((X) > (Y) ? (X) : (Y)) ((X) > (Y) ? (X) : (Y)) | |||
432 | ||||
433 | /* Store all TOP N counters where each has a dynamic length. */ | |||
434 | ||||
435 | static void | |||
436 | write_topn_counters (const struct gcov_ctr_info *ci_ptr, | |||
437 | unsigned t_ix, | |||
438 | gcov_unsigned_t n_counts, | |||
439 | void (*dump_fn) (const void *, unsigned, void *), | |||
440 | void *(*allocate_fn)(unsigned, void *), | |||
441 | void *arg) | |||
442 | { | |||
443 | unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS3; | |||
444 | gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0)((void)(!(n_counts % 3 == 0) ? fancy_abort ("/buildworker/marxinbox-gcc-clang-static-analyzer/build/gcc/../libgcc/libgcov-driver.c" , 444, __FUNCTION__), 0 : 0)); | |||
445 | ||||
446 | /* It can happen in a multi-threaded environment that number of counters is | |||
447 | different from the size of the corresponding linked lists. */ | |||
448 | #define LIST_SIZE_MIN_LENGTH4 * 1024 4 * 1024 | |||
449 | ||||
450 | static unsigned *list_sizes = NULLnullptr; | |||
451 | static unsigned list_size_length = 0; | |||
452 | ||||
453 | if (list_sizes == NULLnullptr || counters > list_size_length) | |||
454 | { | |||
455 | list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters)((4 * 1024) > (2 * counters) ? (4 * 1024) : (2 * counters) ); | |||
456 | #if !defined(inhibit_libc) && HAVE_SYS_MMAN_H1 | |||
457 | list_sizes | |||
458 | = (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned)); | |||
459 | #endif | |||
460 | ||||
461 | /* Malloc fallback. */ | |||
462 | if (list_sizes == NULLnullptr) | |||
463 | list_sizes = | |||
464 | (unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned), | |||
465 | arg); | |||
466 | } | |||
467 | ||||
468 | unsigned pair_total = 0; | |||
469 | ||||
470 | for (unsigned i = 0; i < counters; i++) | |||
471 | { | |||
472 | gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS3 * i + 2]; | |||
473 | unsigned sizes = 0; | |||
474 | ||||
475 | for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__long int)start; | |||
476 | node != NULLnullptr; node = node->next) | |||
477 | ++sizes; | |||
478 | ||||
479 | pair_total += sizes; | |||
480 | list_sizes[i] = sizes; | |||
481 | } | |||
482 | ||||
483 | unsigned disk_size = GCOV_TOPN_DISK_COUNTERS2 * counters + 2 * pair_total; | |||
484 | dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix)(((gcov_unsigned_t)0x01a10000) + ((gcov_unsigned_t)(t_ix) << 17)), dump_fn, arg), | |||
485 | dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size)((disk_size) * 2 * 4), dump_fn, arg); | |||
486 | ||||
487 | for (unsigned i = 0; i < counters; i++) | |||
488 | { | |||
489 | dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS3 * i], dump_fn, arg); | |||
490 | dump_counter (list_sizes[i], dump_fn, arg); | |||
491 | gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS3 * i + 2]; | |||
492 | ||||
493 | unsigned j = 0; | |||
494 | for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__long int)start; | |||
495 | j < list_sizes[i]; node = node->next, j++) | |||
496 | { | |||
497 | dump_counter (node->value, dump_fn, arg); | |||
498 | dump_counter (node->count, dump_fn, arg); | |||
499 | } | |||
500 | } | |||
501 | } | |||
502 | ||||
503 | /* Write counters in GI_PTR and the summary in PRG to a gcda file. In | |||
504 | the case of appending to an existing file, SUMMARY_POS will be non-zero. | |||
505 | We will write the file starting from SUMMAY_POS. */ | |||
506 | ||||
507 | static void | |||
508 | write_one_data (const struct gcov_info *gi_ptr, | |||
509 | const struct gcov_summary *prg_p ATTRIBUTE_UNUSED__attribute__ ((__unused__)), | |||
510 | void (*dump_fn) (const void *, unsigned, void *), | |||
511 | void *(*allocate_fn) (unsigned, void *), | |||
512 | void *arg) | |||
513 | { | |||
514 | unsigned f_ix; | |||
515 | ||||
516 | dump_unsigned (GCOV_DATA_MAGIC((gcov_unsigned_t)0x67636461), dump_fn, arg); | |||
517 | dump_unsigned (GCOV_VERSION((gcov_unsigned_t)0x42333020), dump_fn, arg); | |||
518 | dump_unsigned (gi_ptr->stamp, dump_fn, arg); | |||
519 | dump_unsigned (gi_ptr->checksum, dump_fn, arg); | |||
520 | ||||
521 | #ifdef NEED_L_GCOV | |||
522 | /* Generate whole program statistics. */ | |||
523 | gcov_write_object_summary (prg_p); | |||
524 | #endif | |||
525 | ||||
526 | /* Write execution counts for each function. */ | |||
527 | for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++) | |||
528 | { | |||
529 | #ifdef NEED_L_GCOV | |||
530 | unsigned buffered = 0; | |||
531 | #endif | |||
532 | const struct gcov_fn_info *gfi_ptr; | |||
533 | const struct gcov_ctr_info *ci_ptr; | |||
534 | gcov_unsigned_t length; | |||
535 | unsigned t_ix; | |||
536 | ||||
537 | #ifdef NEED_L_GCOV | |||
538 | if (fn_buffer && fn_buffer->fn_ix == f_ix) | |||
539 | { | |||
540 | /* Buffered data from another program. */ | |||
541 | buffered = 1; | |||
542 | gfi_ptr = &fn_buffer->info; | |||
543 | length = GCOV_TAG_FUNCTION_LENGTH(3 * 4); | |||
544 | } | |||
545 | else | |||
546 | #endif | |||
547 | { | |||
548 | gfi_ptr = gi_ptr->functions[f_ix]; | |||
549 | if (gfi_ptr && gfi_ptr->key == gi_ptr) | |||
550 | length = GCOV_TAG_FUNCTION_LENGTH(3 * 4); | |||
551 | else | |||
552 | length = 0; | |||
553 | } | |||
554 | ||||
555 | dump_unsigned (GCOV_TAG_FUNCTION((gcov_unsigned_t)0x01000000), dump_fn, arg); | |||
556 | dump_unsigned (length, dump_fn, arg); | |||
557 | if (!length) | |||
558 | continue; | |||
559 | ||||
560 | dump_unsigned (gfi_ptr->ident, dump_fn, arg); | |||
561 | dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg); | |||
562 | dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg); | |||
563 | ||||
564 | ci_ptr = gfi_ptr->ctrs; | |||
565 | for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) | |||
566 | { | |||
567 | gcov_position_t n_counts; | |||
568 | ||||
569 | if (!gi_ptr->merge[t_ix]) | |||
570 | continue; | |||
571 | ||||
572 | n_counts = ci_ptr->num; | |||
573 | ||||
574 | if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR) | |||
575 | write_topn_counters (ci_ptr, t_ix, n_counts, dump_fn, allocate_fn, | |||
576 | arg); | |||
577 | else | |||
578 | { | |||
579 | dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix)(((gcov_unsigned_t)0x01a10000) + ((gcov_unsigned_t)(t_ix) << 17)), dump_fn, arg); | |||
580 | if (are_all_counters_zero (ci_ptr)) | |||
581 | /* Do not stream when all counters are zero. */ | |||
582 | dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts)((-n_counts) * 2 * 4), | |||
583 | dump_fn, arg); | |||
584 | else | |||
585 | { | |||
586 | dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts)((n_counts) * 2 * 4), | |||
587 | dump_fn, arg); | |||
588 | for (unsigned i = 0; i < n_counts; i++) | |||
589 | dump_counter (ci_ptr->values[i], dump_fn, arg); | |||
590 | } | |||
591 | } | |||
592 | ||||
593 | ci_ptr++; | |||
594 | } | |||
595 | #ifdef NEED_L_GCOV | |||
596 | if (buffered) | |||
597 | fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); | |||
598 | #endif | |||
599 | } | |||
600 | ||||
601 | dump_unsigned (0, dump_fn, arg); | |||
602 | } | |||
603 | #endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */ | |||
604 | ||||
605 | #ifdef NEED_L_GCOV | |||
606 | /* Dump the coverage counts for one gcov_info object. We merge with existing | |||
607 | counts when possible, to avoid growing the .da files ad infinitum. We use | |||
608 | this program's checksum to make sure we only accumulate whole program | |||
609 | statistics to the correct summary. An object file might be embedded | |||
610 | in two separate programs, and we must keep the two program | |||
611 | summaries separate. */ | |||
612 | ||||
613 | static void | |||
614 | dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf, | |||
615 | unsigned run_counted ATTRIBUTE_UNUSED__attribute__ ((__unused__)), | |||
616 | gcov_type run_max ATTRIBUTE_UNUSED__attribute__ ((__unused__)), int mode) | |||
617 | { | |||
618 | struct gcov_summary summary = {}; | |||
619 | int error; | |||
620 | gcov_unsigned_t tag; | |||
621 | fn_buffer = 0; | |||
622 | ||||
623 | error = gcov_exit_open_gcda_file (gi_ptr, gf, mode); | |||
624 | if (error == -1) | |||
625 | return; | |||
626 | ||||
627 | tag = gcov_read_unsigned (); | |||
628 | if (tag) | |||
629 | { | |||
630 | /* Merge data from file. */ | |||
631 | if (tag != GCOV_DATA_MAGIC((gcov_unsigned_t)0x67636461)) | |||
632 | { | |||
633 | gcov_error (GCOV_PROF_PREFIX"libgcov profiling error:%s:" "Not a gcov data file\n", | |||
634 | gf->filename); | |||
635 | goto read_fatal; | |||
636 | } | |||
637 | error = merge_one_data (gf->filename, gi_ptr, &summary); | |||
638 | if (error == -1) | |||
639 | goto read_fatal; | |||
640 | } | |||
641 | ||||
642 | gcov_rewrite (); | |||
643 | ||||
644 | #if !IN_GCOV_TOOL1 | |||
645 | if (!run_counted) | |||
646 | { | |||
647 | summary.runs++; | |||
648 | summary.sum_max += run_max; | |||
649 | } | |||
650 | #else | |||
651 | summary = gi_ptr->summary; | |||
652 | #endif | |||
653 | ||||
654 | write_one_data (gi_ptr, &summary, gcov_dump_handler, gcov_allocate_handler, | |||
655 | NULLnullptr); | |||
656 | /* fall through */ | |||
657 | ||||
658 | read_fatal:; | |||
659 | while (fn_buffer) | |||
660 | fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); | |||
661 | ||||
662 | if ((error = gcov_close ())) | |||
663 | gcov_error ((error < 0 ? GCOV_PROF_PREFIX"libgcov profiling error:%s:" "Overflow writing\n" | |||
664 | : GCOV_PROF_PREFIX"libgcov profiling error:%s:" "Error writing\n"), gf->filename); | |||
665 | } | |||
666 | ||||
667 | ||||
668 | /* Dump all the coverage counts for the program. It first computes program | |||
669 | summary and then traverses gcov_list list and dumps the gcov_info | |||
670 | objects one by one. Use MODE to open files. */ | |||
671 | ||||
672 | #if !IN_GCOV_TOOL1 | |||
673 | static | |||
674 | #endif | |||
675 | void | |||
676 | gcov_do_dump (struct gcov_info *list, int run_counted, int mode) | |||
677 | { | |||
678 | struct gcov_info *gi_ptr; | |||
679 | struct gcov_filename gf; | |||
680 | ||||
681 | /* Compute run_max of this program run. */ | |||
682 | gcov_type run_max = 0; | |||
683 | for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next) | |||
| ||||
684 | for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++) | |||
685 | { | |||
686 | const struct gcov_ctr_info *cinfo | |||
687 | = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS]; | |||
688 | ||||
689 | for (unsigned i = 0; i < cinfo->num; i++) | |||
690 | if (run_max < cinfo->values[i]) | |||
691 | run_max = cinfo->values[i]; | |||
692 | } | |||
693 | ||||
694 | allocate_filename_struct (&gf); | |||
695 | ||||
696 | /* Now merge each file. */ | |||
697 | for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next) | |||
698 | { | |||
699 | dump_one_gcov (gi_ptr, &gf, run_counted, run_max, mode); | |||
700 | free (gf.filename); | |||
701 | } | |||
702 | ||||
703 | free (gf.prefix); | |||
704 | } | |||
705 | ||||
706 | #if IN_GCOV_TOOL1 | |||
707 | const char * | |||
708 | __attribute__ ((unused)) | |||
709 | gcov_get_filename (struct gcov_info *list) | |||
710 | { | |||
711 | return list->filename; | |||
712 | } | |||
713 | #endif | |||
714 | ||||
715 | #if !IN_GCOV_TOOL1 | |||
716 | void | |||
717 | __gcov_dump_one (struct gcov_root *root) | |||
718 | { | |||
719 | if (root->dumped) | |||
720 | return; | |||
721 | ||||
722 | gcov_do_dump (root->list, root->run_counted, 0); | |||
723 | ||||
724 | root->dumped = 1; | |||
725 | root->run_counted = 1; | |||
726 | } | |||
727 | ||||
728 | /* Per-dynamic-object gcov state. */ | |||
729 | struct gcov_root __gcov_root; | |||
730 | ||||
731 | /* Exactly one of these will be live in the process image. */ | |||
732 | struct gcov_master __gcov_master = | |||
733 | {GCOV_VERSION((gcov_unsigned_t)0x42333020), 0}; | |||
734 | ||||
735 | /* Dynamic pool for gcov_kvp structures. */ | |||
736 | struct gcov_kvp *__gcov_kvp_dynamic_pool; | |||
737 | ||||
738 | /* Index into __gcov_kvp_dynamic_pool array. */ | |||
739 | unsigned __gcov_kvp_dynamic_pool_index; | |||
740 | ||||
741 | /* Size of _gcov_kvp_dynamic_pool array. */ | |||
742 | unsigned __gcov_kvp_dynamic_pool_size; | |||
743 | ||||
744 | void | |||
745 | __gcov_exit (void) | |||
746 | { | |||
747 | __gcov_dump_one (&__gcov_root); | |||
748 | if (__gcov_root.next) | |||
749 | __gcov_root.next->prev = __gcov_root.prev; | |||
750 | if (__gcov_root.prev) | |||
751 | __gcov_root.prev->next = __gcov_root.next; | |||
752 | else | |||
753 | __gcov_master.root = __gcov_root.next; | |||
754 | ||||
755 | gcov_error_exit (); | |||
756 | } | |||
757 | ||||
758 | /* Add a new object file onto the bb chain. Invoked automatically | |||
759 | when running an object file's global ctors. */ | |||
760 | ||||
761 | void | |||
762 | __gcov_init (struct gcov_info *info) | |||
763 | { | |||
764 | if (!info->version || !info->n_functions) | |||
765 | return; | |||
766 | if (gcov_version (info, info->version, 0)) | |||
767 | { | |||
768 | if (!__gcov_root.list) | |||
769 | { | |||
770 | /* Add to master list and at exit function. */ | |||
771 | if (gcov_version (NULLnullptr, __gcov_master.version, "<master>")) | |||
772 | { | |||
773 | __gcov_root.next = __gcov_master.root; | |||
774 | if (__gcov_master.root) | |||
775 | __gcov_master.root->prev = &__gcov_root; | |||
776 | __gcov_master.root = &__gcov_root; | |||
777 | } | |||
778 | } | |||
779 | ||||
780 | info->next = __gcov_root.list; | |||
781 | __gcov_root.list = info; | |||
782 | } | |||
783 | } | |||
784 | #endif /* !IN_GCOV_TOOL */ | |||
785 | #endif /* NEED_L_GCOV */ | |||
786 | ||||
787 | #ifdef NEED_L_GCOV_INFO_TO_GCDA | |||
788 | /* Convert the gcov info to a gcda data stream. It is intended for | |||
789 | freestanding environments which do not support the C library file I/O. */ | |||
790 | ||||
791 | void | |||
792 | __gcov_info_to_gcda (const struct gcov_info *gi_ptr, | |||
793 | void (*filename_fn) (const char *, void *), | |||
794 | void (*dump_fn) (const void *, unsigned, void *), | |||
795 | void *(*allocate_fn) (unsigned, void *), | |||
796 | void *arg) | |||
797 | { | |||
798 | (*filename_fn) (gi_ptr->filename, arg); | |||
799 | write_one_data (gi_ptr, NULLnullptr, dump_fn, allocate_fn, arg); | |||
800 | } | |||
801 | ||||
802 | /* Convert the filename to a gcfn data stream. It is intended for | |||
803 | freestanding environments which do not support the C library file I/O. */ | |||
804 | ||||
805 | void | |||
806 | __gcov_filename_to_gcfn (const char *filename, | |||
807 | void (*dump_fn) (const void *, unsigned, void *), | |||
808 | void *arg) | |||
809 | { | |||
810 | dump_unsigned (GCOV_FILENAME_MAGIC((gcov_unsigned_t)0x6763666e), dump_fn, arg); | |||
811 | dump_unsigned (GCOV_VERSION((gcov_unsigned_t)0x42333020), dump_fn, arg); | |||
812 | dump_string (filename, dump_fn, arg); | |||
813 | } | |||
814 | #endif /* NEED_L_GCOV_INFO_TO_GCDA */ |
1 | /* Routines required for instrumenting a program. */ | ||||
2 | /* Compile this one with gcc. */ | ||||
3 | /* Copyright (C) 1989-2023 Free Software Foundation, Inc. | ||||
4 | |||||
5 | This file is part of GCC. | ||||
6 | |||||
7 | GCC is free software; you can redistribute it and/or modify it under | ||||
8 | the terms of the GNU General Public License as published by the Free | ||||
9 | Software Foundation; either version 3, or (at your option) any later | ||||
10 | version. | ||||
11 | |||||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | ||||
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||||
15 | for more details. | ||||
16 | |||||
17 | Under Section 7 of GPL version 3, you are granted additional | ||||
18 | permissions described in the GCC Runtime Library Exception, version | ||||
19 | 3.1, as published by the Free Software Foundation. | ||||
20 | |||||
21 | You should have received a copy of the GNU General Public License and | ||||
22 | a copy of the GCC Runtime Library Exception along with this program; | ||||
23 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | ||||
24 | <http://www.gnu.org/licenses/>. */ | ||||
25 | |||||
26 | #if !IN_GCOV_TOOL1 | ||||
27 | /* Configured via the GCOV_ERROR_FILE environment variable; | ||||
28 | it will either be stderr, or a file of the user's choosing. | ||||
29 | Non-static to prevent multiple gcov-aware shared objects from | ||||
30 | instantiating their own copies. */ | ||||
31 | FILE *__gcov_error_file = NULLnullptr; | ||||
32 | #endif | ||||
33 | |||||
34 | /* A utility function to populate the __gcov_error_file pointer. | ||||
35 | This should NOT be called outside of the gcov system driver code. */ | ||||
36 | |||||
37 | static FILE * | ||||
38 | get_gcov_error_file (void) | ||||
39 | { | ||||
40 | #if IN_GCOV_TOOL1 | ||||
41 | return stderrstderr; | ||||
42 | #else | ||||
43 | if (!__gcov_error_file) | ||||
44 | { | ||||
45 | const char *gcov_error_filename = getenv ("GCOV_ERROR_FILE"); | ||||
46 | |||||
47 | if (gcov_error_filename) | ||||
48 | __gcov_error_file = fopen (gcov_error_filename, "a"); | ||||
49 | if (!__gcov_error_file) | ||||
50 | __gcov_error_file = stderrstderr; | ||||
51 | } | ||||
52 | return __gcov_error_file; | ||||
53 | #endif | ||||
54 | } | ||||
55 | |||||
56 | /* A utility function for outputting errors. */ | ||||
57 | |||||
58 | static int __attribute__((format(printf, 1, 2))) | ||||
59 | gcov_error (const char *fmt, ...) | ||||
60 | { | ||||
61 | int ret; | ||||
62 | va_list argp; | ||||
63 | |||||
64 | va_start (argp, fmt)__builtin_va_start(argp, fmt); | ||||
65 | FILE *f = get_gcov_error_file (); | ||||
66 | ret = vfprintf (f, fmt, argp); | ||||
67 | va_end (argp)__builtin_va_end(argp); | ||||
68 | |||||
69 | if (getenv ("GCOV_EXIT_AT_ERROR")) | ||||
70 | { | ||||
71 | fprintf (f, "profiling:exiting after an error\n"); | ||||
72 | exit (1); | ||||
73 | } | ||||
74 | |||||
75 | return ret; | ||||
76 | } | ||||
77 | |||||
78 | #if !IN_GCOV_TOOL1 | ||||
79 | static void | ||||
80 | gcov_error_exit (void) | ||||
81 | { | ||||
82 | if (__gcov_error_file && __gcov_error_file != stderrstderr) | ||||
83 | { | ||||
84 | fclose (__gcov_error_file); | ||||
85 | __gcov_error_file = NULLnullptr; | ||||
86 | } | ||||
87 | } | ||||
88 | #endif | ||||
89 | |||||
90 | /* Make sure path component of the given FILENAME exists, create | ||||
91 | missing directories. FILENAME must be writable. | ||||
92 | Returns zero on success, or -1 if an error occurred. */ | ||||
93 | |||||
94 | static int | ||||
95 | create_file_directory (char *filename) | ||||
96 | { | ||||
97 | #if !defined(TARGET_POSIX_IO) && !defined(_WIN32) | ||||
98 | (void) filename; | ||||
99 | return -1; | ||||
100 | #else | ||||
101 | char *s; | ||||
102 | |||||
103 | s = filename; | ||||
104 | |||||
105 | if (HAS_DRIVE_SPEC(s)(0)) | ||||
106 | s += 2; | ||||
107 | if (IS_DIR_SEPARATOR(*s)(((*s) == '/') || (((*s) == '\\') && (0)))) | ||||
108 | ++s; | ||||
109 | for (; *s != '\0'; s++) | ||||
110 | if (IS_DIR_SEPARATOR(*s)(((*s) == '/') || (((*s) == '\\') && (0)))) | ||||
111 | { | ||||
112 | char sep = *s; | ||||
113 | *s = '\0'; | ||||
114 | |||||
115 | /* Try to make directory if it doesn't already exist. */ | ||||
116 | if (access (filename, F_OK0) == -1 | ||||
117 | #ifdef TARGET_POSIX_IO | ||||
118 | && mkdir (filename, 0777) == -1 | ||||
119 | #else | ||||
120 | #ifdef mkdir | ||||
121 | #undef mkdir | ||||
122 | #endif | ||||
123 | && mkdir (filename) == -1 | ||||
124 | #endif | ||||
125 | /* The directory might have been made by another process. */ | ||||
126 | && errno(*__errno_location ()) != EEXIST17) | ||||
127 | { | ||||
128 | gcov_error ("profiling:%s:Cannot create directory\n", filename); | ||||
129 | *s = sep; | ||||
130 | return -1; | ||||
131 | }; | ||||
132 | |||||
133 | *s = sep; | ||||
134 | }; | ||||
135 | return 0; | ||||
136 | #endif | ||||
137 | } | ||||
138 | |||||
139 | /* Replace filename variables in FILENAME. We currently support expansion: | ||||
140 | |||||
141 | %p - process ID | ||||
142 | %q{ENV} - value of environment variable ENV | ||||
143 | */ | ||||
144 | |||||
145 | static char * | ||||
146 | replace_filename_variables (char *filename) | ||||
147 | { | ||||
148 | char buffer[16]; | ||||
149 | char empty[] = ""; | ||||
150 | for (char *p = filename; *p != '\0'; p++) | ||||
151 | { | ||||
152 | unsigned length = strlen (filename); | ||||
153 | if (*p == '%' && *(p + 1) != '\0') | ||||
154 | { | ||||
155 | unsigned start = p - filename; | ||||
156 | p++; | ||||
157 | char *replacement = NULLnullptr; | ||||
158 | switch (*p) | ||||
159 | { | ||||
160 | case 'p': | ||||
161 | sprintf (buffer, "%d", getpid ()); | ||||
162 | replacement = buffer; | ||||
163 | p++; | ||||
164 | break; | ||||
165 | case 'q': | ||||
166 | if (*(p + 1) == '{') | ||||
167 | { | ||||
168 | p += 2; | ||||
169 | char *e = strchr (p, '}'); | ||||
170 | if (e) | ||||
171 | { | ||||
172 | *e = '\0'; | ||||
173 | replacement = getenv (p); | ||||
174 | if (replacement == NULLnullptr) | ||||
175 | replacement = empty; | ||||
176 | p = e + 1; | ||||
177 | } | ||||
178 | else | ||||
179 | return filename; | ||||
180 | } | ||||
181 | break; | ||||
182 | default: | ||||
183 | return filename; | ||||
184 | } | ||||
185 | |||||
186 | /* Concat beginning of the path, replacement and | ||||
187 | ending of the path. */ | ||||
188 | unsigned end = length - (p - filename); | ||||
189 | unsigned repl_length = replacement != NULLnullptr ? strlen (replacement) : 0; | ||||
190 | |||||
191 | char *buffer = (char *)xmallocmalloc (start + end + repl_length + 1); | ||||
192 | char *buffer_ptr = buffer; | ||||
193 | buffer_ptr = (char *)memcpy (buffer_ptr, filename, start); | ||||
194 | buffer_ptr += start; | ||||
195 | if (replacement != NULLnullptr) | ||||
196 | buffer_ptr = (char *)memcpy (buffer_ptr, replacement, repl_length); | ||||
197 | buffer_ptr += repl_length; | ||||
198 | buffer_ptr = (char *)memcpy (buffer_ptr, p, end); | ||||
199 | buffer_ptr += end; | ||||
200 | *buffer_ptr = '\0'; | ||||
201 | |||||
202 | free (filename); | ||||
| |||||
203 | filename = buffer; | ||||
204 | p = buffer + start + repl_length; | ||||
205 | } | ||||
206 | } | ||||
207 | |||||
208 | return filename; | ||||
209 | } | ||||
210 | |||||
211 | static void | ||||
212 | allocate_filename_struct (struct gcov_filename *gf) | ||||
213 | { | ||||
214 | const char *gcov_prefix; | ||||
215 | size_t prefix_length; | ||||
216 | int strip = 0; | ||||
217 | gf->filename = NULLnullptr; | ||||
218 | |||||
219 | { | ||||
220 | /* Check if the level of dirs to strip off specified. */ | ||||
221 | char *tmp = getenv("GCOV_PREFIX_STRIP"); | ||||
222 | if (tmp) | ||||
223 | { | ||||
224 | strip = atoi (tmp); | ||||
225 | /* Do not consider negative values. */ | ||||
226 | if (strip < 0) | ||||
227 | strip = 0; | ||||
228 | } | ||||
229 | } | ||||
230 | gf->strip = strip; | ||||
231 | |||||
232 | /* Get file name relocation prefix. Non-absolute values are ignored. */ | ||||
233 | gcov_prefix = getenv("GCOV_PREFIX"); | ||||
234 | prefix_length = gcov_prefix ? strlen (gcov_prefix) : 0; | ||||
235 | |||||
236 | /* Remove an unnecessary trailing '/' */ | ||||
237 | if (prefix_length && IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1])(((gcov_prefix[prefix_length - 1]) == '/') || (((gcov_prefix[ prefix_length - 1]) == '\\') && (0)))) | ||||
238 | prefix_length--; | ||||
239 | |||||
240 | /* If no prefix was specified and a prefix stip, then we assume | ||||
241 | relative. */ | ||||
242 | if (!prefix_length && gf->strip) | ||||
243 | { | ||||
244 | gcov_prefix = "."; | ||||
245 | prefix_length = 1; | ||||
246 | } | ||||
247 | |||||
248 | /* Allocate and initialize the filename scratch space. */ | ||||
249 | if (prefix_length) | ||||
250 | { | ||||
251 | gf->prefix = (char *) xmallocmalloc (prefix_length + 1); | ||||
252 | char *p = (char *) memcpy (gf->prefix, gcov_prefix, prefix_length); | ||||
253 | *(p + prefix_length) = '\0'; | ||||
254 | } | ||||
255 | else | ||||
256 | gf->prefix = NULLnullptr; | ||||
257 | } | ||||
258 | |||||
259 | /* Open a gcda file specified by GI_FILENAME. | ||||
260 | Return -1 on error. Return 0 on success. */ | ||||
261 | |||||
262 | static int | ||||
263 | gcov_exit_open_gcda_file (struct gcov_info *gi_ptr, | ||||
264 | struct gcov_filename *gf, | ||||
265 | int mode) | ||||
266 | { | ||||
267 | int append_slash = 0; | ||||
268 | const char *fname = gi_ptr->filename; | ||||
269 | |||||
270 | /* Build relocated filename, stripping off leading | ||||
271 | directories from the initial filename if requested. */ | ||||
272 | if (gf->strip
| ||||
273 | { | ||||
274 | const char *probe = fname; | ||||
275 | int level; | ||||
276 | |||||
277 | /* Remove a leading separator, without counting it. */ | ||||
278 | if (IS_DIR_SEPARATOR (*probe)(((*probe) == '/') || (((*probe) == '\\') && (0)))) | ||||
279 | probe++; | ||||
280 | |||||
281 | /* Skip selected directory levels. If we fall off the end, we | ||||
282 | keep the final part. */ | ||||
283 | for (level = gf->strip; *probe && level; probe++) | ||||
284 | if (IS_DIR_SEPARATOR (*probe)(((*probe) == '/') || (((*probe) == '\\') && (0)))) | ||||
285 | { | ||||
286 | fname = probe; | ||||
287 | level--; | ||||
288 | } | ||||
289 | } | ||||
290 | |||||
291 | /* Update complete filename with stripped original. */ | ||||
292 | if (gf->prefix
| ||||
293 | { | ||||
294 | /* Avoid to add multiple drive letters into combined path. */ | ||||
295 | if (HAS_DRIVE_SPEC(fname)(0)) | ||||
296 | fname += 2; | ||||
297 | |||||
298 | if (!IS_DIR_SEPARATOR (*fname)(((*fname) == '/') || (((*fname) == '\\') && (0)))) | ||||
299 | append_slash = 1; | ||||
300 | } | ||||
301 | |||||
302 | size_t prefix_length = gf->prefix
| ||||
303 | gf->filename = (char *) xmallocmalloc (prefix_length + strlen (fname) + 2); | ||||
304 | *gf->filename = '\0'; | ||||
305 | if (prefix_length) | ||||
306 | strcat (gf->filename, gf->prefix); | ||||
307 | if (append_slash
| ||||
308 | *gf->filename++ = '/'; | ||||
309 | strcat (gf->filename, fname); | ||||
310 | |||||
311 | gf->filename = replace_filename_variables (gf->filename); | ||||
312 | |||||
313 | if (!gcov_open (gf->filename, mode)) | ||||
314 | { | ||||
315 | /* Open failed likely due to missed directory. | ||||
316 | Create directory and retry to open file. */ | ||||
317 | if (create_file_directory (gf->filename)) | ||||
318 | { | ||||
319 | fprintf (stderrstderr, "profiling:%s:Skip\n", gf->filename); | ||||
320 | return -1; | ||||
321 | } | ||||
322 | if (!gcov_open (gf->filename, mode)) | ||||
323 | { | ||||
324 | fprintf (stderrstderr, "profiling:%s:Cannot open\n", gf->filename); | ||||
325 | return -1; | ||||
326 | } | ||||
327 | } | ||||
328 | |||||
329 | return 0; | ||||
330 | } |