1/* Routines required for instrumenting a program.  */
2/* Compile this one with gcc.  */
3/* Copyright (C) 1989-2015 Free Software Foundation, Inc.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see 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
32void __gcov_flush (void) {}
33#endif
34
35#ifdef L_gcov_reset
36void __gcov_reset (void) {}
37#endif
38
39#ifdef L_gcov_dump
40void __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
54extern __gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
55extern __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
64static void
65init_mx (void)
66{
67  __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
68}
69
70static void
71init_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
82void
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
100static void
101gcov_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
132void
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
147ALIAS_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
155void
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
167ALIAS_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
175pid_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
191int
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
222int
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
253int
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
286int
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
298int
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
310int
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 */
318