1 /* Routines required for instrumenting a program. */
2 /* Compile this one with gcc. */
3 /* Copyright (C) 1989-2021 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 "gthr.h"
28
29 #if defined(inhibit_libc)
30
31 #ifdef L_gcov_reset
__gcov_reset(void)32 void __gcov_reset (void) {}
33 #endif
34
35 #ifdef L_gcov_dump
__gcov_dump(void)36 void __gcov_dump (void) {}
37 #endif
38
39 #else
40
41 extern __gthread_mutex_t __gcov_mx ATTRIBUTE_HIDDEN;
42
43 #ifdef L_gcov_lock_unlock
44 #ifdef __GTHREAD_MUTEX_INIT
45 __gthread_mutex_t __gcov_mx = __GTHREAD_MUTEX_INIT;
46 #define init_mx_once()
47 #else
48 __gthread_mutex_t __gcov_mx;
49
50 static void
init_mx(void)51 init_mx (void)
52 {
53 __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_mx);
54 }
55
56 static void
init_mx_once(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
64 /* Lock critical section for __gcov_dump and __gcov_reset functions. */
65
66 void
__gcov_lock(void)67 __gcov_lock (void)
68 {
69 init_mx_once ();
70 __gthread_mutex_lock (&__gcov_mx);
71 }
72
73 /* Unlock critical section for __gcov_dump and __gcov_reset functions. */
74
75 void
__gcov_unlock(void)76 __gcov_unlock (void)
77 {
78 __gthread_mutex_unlock (&__gcov_mx);
79 }
80 #endif
81
82 #ifdef L_gcov_reset
83
84 /* Reset all counters to zero. */
85
86 static void
gcov_clear(const struct gcov_info * list)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
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
__gcov_reset_int(void)119 __gcov_reset_int (void)
120 {
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 }
131 }
132
133 /* Exported function __gcov_reset. */
134
135 void
__gcov_reset(void)136 __gcov_reset (void)
137 {
138 __gcov_lock ();
139
140 __gcov_reset_int ();
141
142 __gcov_unlock ();
143 }
144
145 #endif /* L_gcov_reset */
146
147 #ifdef L_gcov_dump
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
__gcov_dump_int(void)152 __gcov_dump_int (void)
153 {
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);
161 }
162
163 /* Exported function __gcov_dump. */
164
165 void
__gcov_dump(void)166 __gcov_dump (void)
167 {
168 __gcov_lock ();
169
170 __gcov_dump_int ();
171
172 __gcov_unlock ();
173 }
174
175 #endif /* L_gcov_dump */
176
177 #ifdef L_gcov_fork
178 /* A wrapper for the fork function. We reset counters in the child
179 so that they are not counted twice. */
180
181 pid_t
__gcov_fork(void)182 __gcov_fork (void)
183 {
184 pid_t pid;
185 pid = fork ();
186 if (pid == 0)
187 {
188 __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_mx);
189 /* We do not need locking as we are the only thread in the child. */
190 __gcov_reset_int ();
191 }
192 return pid;
193 }
194 #endif
195
196 #ifdef L_gcov_execl
197 /* A wrapper for the execl function. Flushes the accumulated
198 profiling data, so that they are not lost. */
199
200 int
__gcov_execl(const char * path,char * arg,...)201 __gcov_execl (const char *path, char *arg, ...)
202 {
203 va_list ap, aq;
204 unsigned i, length;
205 char **args;
206
207 /* Dump counters only, they will be lost after exec. */
208 __gcov_dump ();
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
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;
228 }
229 #endif
230
231 #ifdef L_gcov_execlp
232 /* A wrapper for the execlp function. Flushes the accumulated
233 profiling data, so that they are not lost. */
234
235 int
__gcov_execlp(const char * path,char * arg,...)236 __gcov_execlp (const char *path, char *arg, ...)
237 {
238 va_list ap, aq;
239 unsigned i, length;
240 char **args;
241
242 /* Dump counters only, they will be lost after exec. */
243 __gcov_dump ();
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
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;
263 }
264 #endif
265
266 #ifdef L_gcov_execle
267 /* A wrapper for the execle function. Flushes the accumulated
268 profiling data, so that they are not lost. */
269
270 int
__gcov_execle(const char * path,char * arg,...)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
278 /* Dump counters only, they will be lost after exec. */
279 __gcov_dump ();
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
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;
300 }
301 #endif
302
303 #ifdef L_gcov_execv
304 /* A wrapper for the execv function. Flushes the accumulated
305 profiling data, so that they are not lost. */
306
307 int
__gcov_execv(const char * path,char * const argv[])308 __gcov_execv (const char *path, char *const argv[])
309 {
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;
316 }
317 #endif
318
319 #ifdef L_gcov_execvp
320 /* A wrapper for the execvp function. Flushes the accumulated
321 profiling data, so that they are not lost. */
322
323 int
__gcov_execvp(const char * path,char * const argv[])324 __gcov_execvp (const char *path, char *const argv[])
325 {
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;
332 }
333 #endif
334
335 #ifdef L_gcov_execve
336 /* A wrapper for the execve function. Flushes the accumulated
337 profiling data, so that they are not lost. */
338
339 int
__gcov_execve(const char * path,char * const argv[],char * const envp[])340 __gcov_execve (const char *path, char *const argv[], char *const envp[])
341 {
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;
348 }
349 #endif
350 #endif /* inhibit_libc */
351