]>
Commit | Line | Data |
---|---|---|
d6d3f033 RX |
1 | /* Routines required for instrumenting a program. */ |
2 | /* Compile this one with gcc. */ | |
83ffe9cd | 3 | /* Copyright (C) 1989-2023 Free Software Foundation, Inc. |
d6d3f033 RX |
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 | ||
40d6b753 | 26 | #include "libgcov.h" |
d6d3f033 RX |
27 | #include "gthr.h" |
28 | ||
d6d3f033 RX |
29 | #if defined(inhibit_libc) |
30 | ||
d6d3f033 RX |
31 | #ifdef L_gcov_reset |
32 | void __gcov_reset (void) {} | |
33 | #endif | |
34 | ||
35 | #ifdef L_gcov_dump | |
36 | void __gcov_dump (void) {} | |
37 | #endif | |
38 | ||
39 | #else | |
40 | ||
811b7636 | 41 | extern __gthread_mutex_t __gcov_mx ATTRIBUTE_HIDDEN; |
d6d3f033 | 42 | |
d39f7dc8 | 43 | #ifdef L_gcov_lock_unlock |
d6d3f033 | 44 | #ifdef __GTHREAD_MUTEX_INIT |
811b7636 | 45 | __gthread_mutex_t __gcov_mx = __GTHREAD_MUTEX_INIT; |
d6d3f033 RX |
46 | #define init_mx_once() |
47 | #else | |
811b7636 | 48 | __gthread_mutex_t __gcov_mx; |
d6d3f033 RX |
49 | |
50 | static void | |
51 | init_mx (void) | |
52 | { | |
811b7636 | 53 | __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_mx); |
d6d3f033 | 54 | } |
b98a872b | 55 | |
d6d3f033 RX |
56 | static void |
57 | init_mx_once (void) | |
58 | { | |
59 | static __gthread_once_t once = __GTHREAD_ONCE_INIT; | |
60 | __gthread_once (&once, init_mx); | |
61 | } | |
62 | #endif | |
63 | ||
d39f7dc8 ML |
64 | /* Lock critical section for __gcov_dump and __gcov_reset functions. */ |
65 | ||
66 | void | |
67 | __gcov_lock (void) | |
68 | { | |
69 | init_mx_once (); | |
811b7636 | 70 | __gthread_mutex_lock (&__gcov_mx); |
d39f7dc8 ML |
71 | } |
72 | ||
73 | /* Unlock critical section for __gcov_dump and __gcov_reset functions. */ | |
74 | ||
75 | void | |
76 | __gcov_unlock (void) | |
77 | { | |
811b7636 | 78 | __gthread_mutex_unlock (&__gcov_mx); |
d39f7dc8 ML |
79 | } |
80 | #endif | |
81 | ||
d6d3f033 RX |
82 | #ifdef L_gcov_reset |
83 | ||
4303c581 NS |
84 | /* Reset all counters to zero. */ |
85 | ||
86 | static void | |
87 | gcov_clear (const struct gcov_info *list) | |
88 | { | |
89 | const struct gcov_info *gi_ptr; | |
90 | ||
91 | for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next) | |
92 | { | |
93 | unsigned f_ix; | |
94 | ||
95 | for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) | |
96 | { | |
97 | unsigned t_ix; | |
98 | const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; | |
99 | ||
100 | if (!gfi_ptr || gfi_ptr->key != gi_ptr) | |
101 | continue; | |
102 | const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs; | |
103 | for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) | |
104 | { | |
105 | if (!gi_ptr->merge[t_ix]) | |
106 | continue; | |
107 | ||
108 | memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num); | |
109 | ci_ptr++; | |
110 | } | |
111 | } | |
112 | } | |
113 | } | |
114 | ||
d6d3f033 RX |
115 | /* Function that can be called from application to reset counters to zero, |
116 | in order to collect profile in region of interest. */ | |
117 | ||
118 | void | |
b20ee094 | 119 | __gcov_reset_int (void) |
d6d3f033 | 120 | { |
cadb2b96 NS |
121 | struct gcov_root *root; |
122 | ||
123 | /* If we're compatible with the master, iterate over everything, | |
124 | otherise just do us. */ | |
125 | for (root = __gcov_master.version == GCOV_VERSION | |
126 | ? __gcov_master.root : &__gcov_root; root; root = root->next) | |
127 | { | |
128 | gcov_clear (root->list); | |
129 | root->dumped = 0; | |
130 | } | |
d6d3f033 RX |
131 | } |
132 | ||
d39f7dc8 ML |
133 | /* Exported function __gcov_reset. */ |
134 | ||
135 | void | |
136 | __gcov_reset (void) | |
137 | { | |
138 | __gcov_lock (); | |
139 | ||
140 | __gcov_reset_int (); | |
141 | ||
142 | __gcov_unlock (); | |
143 | } | |
b20ee094 | 144 | |
d6d3f033 RX |
145 | #endif /* L_gcov_reset */ |
146 | ||
147 | #ifdef L_gcov_dump | |
d6d3f033 RX |
148 | /* Function that can be called from application to write profile collected |
149 | so far, in order to collect profile in region of interest. */ | |
150 | ||
151 | void | |
cadb2b96 | 152 | __gcov_dump_int (void) |
d6d3f033 | 153 | { |
cadb2b96 NS |
154 | struct gcov_root *root; |
155 | ||
156 | /* If we're compatible with the master, iterate over everything, | |
157 | otherise just do us. */ | |
158 | for (root = __gcov_master.version == GCOV_VERSION | |
159 | ? __gcov_master.root : &__gcov_root; root; root = root->next) | |
160 | __gcov_dump_one (root); | |
d6d3f033 RX |
161 | } |
162 | ||
d39f7dc8 ML |
163 | /* Exported function __gcov_dump. */ |
164 | ||
165 | void | |
166 | __gcov_dump (void) | |
167 | { | |
168 | __gcov_lock (); | |
169 | ||
170 | __gcov_dump_int (); | |
171 | ||
172 | __gcov_unlock (); | |
173 | } | |
cadb2b96 | 174 | |
d6d3f033 RX |
175 | #endif /* L_gcov_dump */ |
176 | ||
d6d3f033 | 177 | #ifdef L_gcov_fork |
c0532db4 ML |
178 | /* A wrapper for the fork function. We reset counters in the child |
179 | so that they are not counted twice. */ | |
d6d3f033 RX |
180 | |
181 | pid_t | |
182 | __gcov_fork (void) | |
183 | { | |
184 | pid_t pid; | |
d6d3f033 RX |
185 | pid = fork (); |
186 | if (pid == 0) | |
c0532db4 | 187 | { |
811b7636 | 188 | __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_mx); |
c0532db4 ML |
189 | /* We do not need locking as we are the only thread in the child. */ |
190 | __gcov_reset_int (); | |
191 | } | |
d6d3f033 RX |
192 | return pid; |
193 | } | |
194 | #endif | |
195 | ||
196 | #ifdef L_gcov_execl | |
cadb2b96 NS |
197 | /* A wrapper for the execl function. Flushes the accumulated |
198 | profiling data, so that they are not lost. */ | |
d6d3f033 RX |
199 | |
200 | int | |
201 | __gcov_execl (const char *path, char *arg, ...) | |
202 | { | |
203 | va_list ap, aq; | |
204 | unsigned i, length; | |
205 | char **args; | |
206 | ||
c0532db4 ML |
207 | /* Dump counters only, they will be lost after exec. */ |
208 | __gcov_dump (); | |
d6d3f033 RX |
209 | |
210 | va_start (ap, arg); | |
211 | va_copy (aq, ap); | |
212 | ||
213 | length = 2; | |
214 | while (va_arg (ap, char *)) | |
215 | length++; | |
216 | va_end (ap); | |
217 | ||
218 | args = (char **) alloca (length * sizeof (void *)); | |
219 | args[0] = arg; | |
220 | for (i = 1; i < length; i++) | |
221 | args[i] = va_arg (aq, char *); | |
222 | va_end (aq); | |
223 | ||
c0532db4 ML |
224 | int ret = execv (path, args); |
225 | /* We reach this code only when execv fails, reset counter then here. */ | |
226 | __gcov_reset (); | |
227 | return ret; | |
d6d3f033 RX |
228 | } |
229 | #endif | |
230 | ||
231 | #ifdef L_gcov_execlp | |
b98a872b NS |
232 | /* A wrapper for the execlp function. Flushes the accumulated |
233 | profiling data, so that they are not lost. */ | |
d6d3f033 RX |
234 | |
235 | int | |
236 | __gcov_execlp (const char *path, char *arg, ...) | |
237 | { | |
238 | va_list ap, aq; | |
239 | unsigned i, length; | |
240 | char **args; | |
241 | ||
c0532db4 ML |
242 | /* Dump counters only, they will be lost after exec. */ |
243 | __gcov_dump (); | |
d6d3f033 RX |
244 | |
245 | va_start (ap, arg); | |
246 | va_copy (aq, ap); | |
247 | ||
248 | length = 2; | |
249 | while (va_arg (ap, char *)) | |
250 | length++; | |
251 | va_end (ap); | |
252 | ||
253 | args = (char **) alloca (length * sizeof (void *)); | |
254 | args[0] = arg; | |
255 | for (i = 1; i < length; i++) | |
256 | args[i] = va_arg (aq, char *); | |
257 | va_end (aq); | |
258 | ||
c0532db4 ML |
259 | int ret = execvp (path, args); |
260 | /* We reach this code only when execv fails, reset counter then here. */ | |
261 | __gcov_reset (); | |
262 | return ret; | |
d6d3f033 RX |
263 | } |
264 | #endif | |
265 | ||
266 | #ifdef L_gcov_execle | |
b98a872b NS |
267 | /* A wrapper for the execle function. Flushes the accumulated |
268 | profiling data, so that they are not lost. */ | |
d6d3f033 RX |
269 | |
270 | int | |
271 | __gcov_execle (const char *path, char *arg, ...) | |
272 | { | |
273 | va_list ap, aq; | |
274 | unsigned i, length; | |
275 | char **args; | |
276 | char **envp; | |
277 | ||
c0532db4 ML |
278 | /* Dump counters only, they will be lost after exec. */ |
279 | __gcov_dump (); | |
d6d3f033 RX |
280 | |
281 | va_start (ap, arg); | |
282 | va_copy (aq, ap); | |
283 | ||
284 | length = 2; | |
285 | while (va_arg (ap, char *)) | |
286 | length++; | |
287 | va_end (ap); | |
288 | ||
289 | args = (char **) alloca (length * sizeof (void *)); | |
290 | args[0] = arg; | |
291 | for (i = 1; i < length; i++) | |
292 | args[i] = va_arg (aq, char *); | |
293 | envp = va_arg (aq, char **); | |
294 | va_end (aq); | |
295 | ||
c0532db4 ML |
296 | int ret = execve (path, args, envp); |
297 | /* We reach this code only when execv fails, reset counter then here. */ | |
298 | __gcov_reset (); | |
299 | return ret; | |
d6d3f033 RX |
300 | } |
301 | #endif | |
302 | ||
303 | #ifdef L_gcov_execv | |
b98a872b NS |
304 | /* A wrapper for the execv function. Flushes the accumulated |
305 | profiling data, so that they are not lost. */ | |
d6d3f033 RX |
306 | |
307 | int | |
308 | __gcov_execv (const char *path, char *const argv[]) | |
309 | { | |
c0532db4 ML |
310 | /* Dump counters only, they will be lost after exec. */ |
311 | __gcov_dump (); | |
312 | int ret = execv (path, argv); | |
313 | /* We reach this code only when execv fails, reset counter then here. */ | |
314 | __gcov_reset (); | |
315 | return ret; | |
d6d3f033 RX |
316 | } |
317 | #endif | |
318 | ||
319 | #ifdef L_gcov_execvp | |
b98a872b NS |
320 | /* A wrapper for the execvp function. Flushes the accumulated |
321 | profiling data, so that they are not lost. */ | |
d6d3f033 RX |
322 | |
323 | int | |
324 | __gcov_execvp (const char *path, char *const argv[]) | |
325 | { | |
c0532db4 ML |
326 | /* Dump counters only, they will be lost after exec. */ |
327 | __gcov_dump (); | |
328 | int ret = execvp (path, argv); | |
329 | /* We reach this code only when execv fails, reset counter then here. */ | |
330 | __gcov_reset (); | |
331 | return ret; | |
d6d3f033 RX |
332 | } |
333 | #endif | |
334 | ||
335 | #ifdef L_gcov_execve | |
b98a872b NS |
336 | /* A wrapper for the execve function. Flushes the accumulated |
337 | profiling data, so that they are not lost. */ | |
d6d3f033 RX |
338 | |
339 | int | |
340 | __gcov_execve (const char *path, char *const argv[], char *const envp[]) | |
341 | { | |
c0532db4 ML |
342 | /* Dump counters only, they will be lost after exec. */ |
343 | __gcov_dump (); | |
344 | int ret = execve (path, argv, envp); | |
345 | /* We reach this code only when execv fails, reset counter then here. */ | |
346 | __gcov_reset (); | |
347 | return ret; | |
d6d3f033 RX |
348 | } |
349 | #endif | |
350 | #endif /* inhibit_libc */ |