111
|
1 /* Routines required for instrumenting a program. */
|
|
2 /* Compile this one with gcc. */
|
145
|
3 /* Copyright (C) 1989-2020 Free Software Foundation, Inc.
|
111
|
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_flush
|
|
32 void __gcov_flush (void) {}
|
|
33 #endif
|
|
34
|
|
35 #ifdef L_gcov_reset
|
|
36 void __gcov_reset (void) {}
|
|
37 #endif
|
|
38
|
|
39 #ifdef L_gcov_dump
|
|
40 void __gcov_dump (void) {}
|
|
41 #endif
|
|
42
|
|
43 #else
|
|
44
|
|
45 /* Some functions we want to bind in this dynamic object, but have an
|
|
46 overridable global alias. Unfortunately not all targets support
|
|
47 aliases, so we just have a forwarding function. That'll be tail
|
|
48 called, so the cost is a single jump instruction.*/
|
|
49
|
|
50 #define ALIAS_void_fn(src,dst) \
|
|
51 void dst (void) \
|
|
52 { src (); }
|
|
53
|
|
54 extern __gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
|
|
55 extern __gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
|
|
56
|
|
57 #ifdef L_gcov_flush
|
|
58 #ifdef __GTHREAD_MUTEX_INIT
|
|
59 __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
|
|
60 #define init_mx_once()
|
|
61 #else
|
|
62 __gthread_mutex_t __gcov_flush_mx;
|
|
63
|
|
64 static void
|
|
65 init_mx (void)
|
|
66 {
|
|
67 __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
|
|
68 }
|
|
69
|
|
70 static void
|
|
71 init_mx_once (void)
|
|
72 {
|
|
73 static __gthread_once_t once = __GTHREAD_ONCE_INIT;
|
|
74 __gthread_once (&once, init_mx);
|
|
75 }
|
|
76 #endif
|
|
77
|
|
78 /* Called before fork or exec - write out profile information gathered so
|
|
79 far and reset it to zero. This avoids duplication or loss of the
|
|
80 profile information gathered so far. */
|
|
81
|
|
82 void
|
|
83 __gcov_flush (void)
|
|
84 {
|
|
85 init_mx_once ();
|
|
86 __gthread_mutex_lock (&__gcov_flush_mx);
|
|
87
|
|
88 __gcov_dump_int ();
|
|
89 __gcov_reset_int ();
|
|
90
|
|
91 __gthread_mutex_unlock (&__gcov_flush_mx);
|
|
92 }
|
|
93
|
|
94 #endif /* L_gcov_flush */
|
|
95
|
|
96 #ifdef L_gcov_reset
|
|
97
|
|
98 /* Reset all counters to zero. */
|
|
99
|
|
100 static void
|
|
101 gcov_clear (const struct gcov_info *list)
|
|
102 {
|
|
103 const struct gcov_info *gi_ptr;
|
|
104
|
|
105 for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
|
|
106 {
|
|
107 unsigned f_ix;
|
|
108
|
|
109 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
|
|
110 {
|
|
111 unsigned t_ix;
|
|
112 const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
|
|
113
|
|
114 if (!gfi_ptr || gfi_ptr->key != gi_ptr)
|
|
115 continue;
|
|
116 const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
|
|
117 for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
|
|
118 {
|
|
119 if (!gi_ptr->merge[t_ix])
|
|
120 continue;
|
|
121
|
|
122 memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
|
|
123 ci_ptr++;
|
|
124 }
|
|
125 }
|
|
126 }
|
|
127 }
|
|
128
|
|
129 /* Function that can be called from application to reset counters to zero,
|
|
130 in order to collect profile in region of interest. */
|
|
131
|
|
132 void
|
|
133 __gcov_reset_int (void)
|
|
134 {
|
|
135 struct gcov_root *root;
|
|
136
|
|
137 /* If we're compatible with the master, iterate over everything,
|
|
138 otherise just do us. */
|
|
139 for (root = __gcov_master.version == GCOV_VERSION
|
|
140 ? __gcov_master.root : &__gcov_root; root; root = root->next)
|
|
141 {
|
|
142 gcov_clear (root->list);
|
|
143 root->dumped = 0;
|
|
144 }
|
|
145 }
|
|
146
|
|
147 ALIAS_void_fn (__gcov_reset_int, __gcov_reset);
|
|
148
|
|
149 #endif /* L_gcov_reset */
|
|
150
|
|
151 #ifdef L_gcov_dump
|
|
152 /* Function that can be called from application to write profile collected
|
|
153 so far, in order to collect profile in region of interest. */
|
|
154
|
|
155 void
|
|
156 __gcov_dump_int (void)
|
|
157 {
|
|
158 struct gcov_root *root;
|
|
159
|
|
160 /* If we're compatible with the master, iterate over everything,
|
|
161 otherise just do us. */
|
|
162 for (root = __gcov_master.version == GCOV_VERSION
|
|
163 ? __gcov_master.root : &__gcov_root; root; root = root->next)
|
|
164 __gcov_dump_one (root);
|
|
165 }
|
|
166
|
|
167 ALIAS_void_fn (__gcov_dump_int, __gcov_dump);
|
|
168
|
|
169 #endif /* L_gcov_dump */
|
|
170
|
|
171 #ifdef L_gcov_fork
|
|
172 /* A wrapper for the fork function. Flushes the accumulated profiling data, so
|
|
173 that they are not counted twice. */
|
|
174
|
|
175 pid_t
|
|
176 __gcov_fork (void)
|
|
177 {
|
|
178 pid_t pid;
|
|
179 __gcov_flush ();
|
|
180 pid = fork ();
|
|
181 if (pid == 0)
|
|
182 __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
|
|
183 return pid;
|
|
184 }
|
|
185 #endif
|
|
186
|
|
187 #ifdef L_gcov_execl
|
|
188 /* A wrapper for the execl function. Flushes the accumulated
|
|
189 profiling data, so that they are not lost. */
|
|
190
|
|
191 int
|
|
192 __gcov_execl (const char *path, char *arg, ...)
|
|
193 {
|
|
194 va_list ap, aq;
|
|
195 unsigned i, length;
|
|
196 char **args;
|
|
197
|
|
198 __gcov_flush ();
|
|
199
|
|
200 va_start (ap, arg);
|
|
201 va_copy (aq, ap);
|
|
202
|
|
203 length = 2;
|
|
204 while (va_arg (ap, char *))
|
|
205 length++;
|
|
206 va_end (ap);
|
|
207
|
|
208 args = (char **) alloca (length * sizeof (void *));
|
|
209 args[0] = arg;
|
|
210 for (i = 1; i < length; i++)
|
|
211 args[i] = va_arg (aq, char *);
|
|
212 va_end (aq);
|
|
213
|
|
214 return execv (path, args);
|
|
215 }
|
|
216 #endif
|
|
217
|
|
218 #ifdef L_gcov_execlp
|
|
219 /* A wrapper for the execlp function. Flushes the accumulated
|
|
220 profiling data, so that they are not lost. */
|
|
221
|
|
222 int
|
|
223 __gcov_execlp (const char *path, char *arg, ...)
|
|
224 {
|
|
225 va_list ap, aq;
|
|
226 unsigned i, length;
|
|
227 char **args;
|
|
228
|
|
229 __gcov_flush ();
|
|
230
|
|
231 va_start (ap, arg);
|
|
232 va_copy (aq, ap);
|
|
233
|
|
234 length = 2;
|
|
235 while (va_arg (ap, char *))
|
|
236 length++;
|
|
237 va_end (ap);
|
|
238
|
|
239 args = (char **) alloca (length * sizeof (void *));
|
|
240 args[0] = arg;
|
|
241 for (i = 1; i < length; i++)
|
|
242 args[i] = va_arg (aq, char *);
|
|
243 va_end (aq);
|
|
244
|
|
245 return execvp (path, args);
|
|
246 }
|
|
247 #endif
|
|
248
|
|
249 #ifdef L_gcov_execle
|
|
250 /* A wrapper for the execle function. Flushes the accumulated
|
|
251 profiling data, so that they are not lost. */
|
|
252
|
|
253 int
|
|
254 __gcov_execle (const char *path, char *arg, ...)
|
|
255 {
|
|
256 va_list ap, aq;
|
|
257 unsigned i, length;
|
|
258 char **args;
|
|
259 char **envp;
|
|
260
|
|
261 __gcov_flush ();
|
|
262
|
|
263 va_start (ap, arg);
|
|
264 va_copy (aq, ap);
|
|
265
|
|
266 length = 2;
|
|
267 while (va_arg (ap, char *))
|
|
268 length++;
|
|
269 va_end (ap);
|
|
270
|
|
271 args = (char **) alloca (length * sizeof (void *));
|
|
272 args[0] = arg;
|
|
273 for (i = 1; i < length; i++)
|
|
274 args[i] = va_arg (aq, char *);
|
|
275 envp = va_arg (aq, char **);
|
|
276 va_end (aq);
|
|
277
|
|
278 return execve (path, args, envp);
|
|
279 }
|
|
280 #endif
|
|
281
|
|
282 #ifdef L_gcov_execv
|
|
283 /* A wrapper for the execv function. Flushes the accumulated
|
|
284 profiling data, so that they are not lost. */
|
|
285
|
|
286 int
|
|
287 __gcov_execv (const char *path, char *const argv[])
|
|
288 {
|
|
289 __gcov_flush ();
|
|
290 return execv (path, argv);
|
|
291 }
|
|
292 #endif
|
|
293
|
|
294 #ifdef L_gcov_execvp
|
|
295 /* A wrapper for the execvp function. Flushes the accumulated
|
|
296 profiling data, so that they are not lost. */
|
|
297
|
|
298 int
|
|
299 __gcov_execvp (const char *path, char *const argv[])
|
|
300 {
|
|
301 __gcov_flush ();
|
|
302 return execvp (path, argv);
|
|
303 }
|
|
304 #endif
|
|
305
|
|
306 #ifdef L_gcov_execve
|
|
307 /* A wrapper for the execve function. Flushes the accumulated
|
|
308 profiling data, so that they are not lost. */
|
|
309
|
|
310 int
|
|
311 __gcov_execve (const char *path, char *const argv[], char *const envp[])
|
|
312 {
|
|
313 __gcov_flush ();
|
|
314 return execve (path, argv, envp);
|
|
315 }
|
|
316 #endif
|
|
317 #endif /* inhibit_libc */
|