1404b540aSrobert /* Routines required for instrumenting a program. */
2404b540aSrobert /* Compile this one with gcc. */
3404b540aSrobert /* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
4404b540aSrobert 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
5404b540aSrobert
6404b540aSrobert This file is part of GCC.
7404b540aSrobert
8404b540aSrobert GCC is free software; you can redistribute it and/or modify it under
9404b540aSrobert the terms of the GNU General Public License as published by the Free
10404b540aSrobert Software Foundation; either version 2, or (at your option) any later
11404b540aSrobert version.
12404b540aSrobert
13404b540aSrobert In addition to the permissions in the GNU General Public License, the
14404b540aSrobert Free Software Foundation gives you unlimited permission to link the
15404b540aSrobert compiled version of this file into combinations with other programs,
16404b540aSrobert and to distribute those combinations without any restriction coming
17404b540aSrobert from the use of this file. (The General Public License restrictions
18404b540aSrobert do apply in other respects; for example, they cover modification of
19404b540aSrobert the file, and distribution when not linked into a combine
20404b540aSrobert executable.)
21404b540aSrobert
22404b540aSrobert GCC is distributed in the hope that it will be useful, but WITHOUT ANY
23404b540aSrobert WARRANTY; without even the implied warranty of MERCHANTABILITY or
24404b540aSrobert FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25404b540aSrobert for more details.
26404b540aSrobert
27404b540aSrobert You should have received a copy of the GNU General Public License
28404b540aSrobert along with GCC; see the file COPYING. If not, write to the Free
29404b540aSrobert Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
30404b540aSrobert 02110-1301, USA. */
31404b540aSrobert
32404b540aSrobert #include "tconfig.h"
33404b540aSrobert #include "tsystem.h"
34404b540aSrobert #include "coretypes.h"
35404b540aSrobert #include "tm.h"
36404b540aSrobert
37404b540aSrobert #if defined(inhibit_libc)
38404b540aSrobert #define IN_LIBGCOV (-1)
39404b540aSrobert #else
40404b540aSrobert #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
41404b540aSrobert #include <stdio.h>
42404b540aSrobert #define IN_LIBGCOV 1
43404b540aSrobert #if defined(L_gcov)
44404b540aSrobert #define GCOV_LINKAGE /* nothing */
45404b540aSrobert #endif
46404b540aSrobert #endif
47404b540aSrobert #include "gcov-io.h"
48404b540aSrobert
49404b540aSrobert #if defined(inhibit_libc)
50404b540aSrobert /* If libc and its header files are not available, provide dummy functions. */
51404b540aSrobert
52404b540aSrobert #ifdef L_gcov
__gcov_init(struct gcov_info * p)53404b540aSrobert void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
__gcov_flush(void)54404b540aSrobert void __gcov_flush (void) {}
55404b540aSrobert #endif
56404b540aSrobert
57404b540aSrobert #ifdef L_gcov_merge_add
__gcov_merge_add(gcov_type * counters,unsigned n_counters)58404b540aSrobert void __gcov_merge_add (gcov_type *counters __attribute__ ((unused)),
59404b540aSrobert unsigned n_counters __attribute__ ((unused))) {}
60404b540aSrobert #endif
61404b540aSrobert
62404b540aSrobert #ifdef L_gcov_merge_single
__gcov_merge_single(gcov_type * counters,unsigned n_counters)63404b540aSrobert void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)),
64404b540aSrobert unsigned n_counters __attribute__ ((unused))) {}
65404b540aSrobert #endif
66404b540aSrobert
67404b540aSrobert #ifdef L_gcov_merge_delta
__gcov_merge_delta(gcov_type * counters,unsigned n_counters)68404b540aSrobert void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)),
69404b540aSrobert unsigned n_counters __attribute__ ((unused))) {}
70404b540aSrobert #endif
71404b540aSrobert
72404b540aSrobert #else
73404b540aSrobert
74404b540aSrobert #include <string.h>
75404b540aSrobert #if GCOV_LOCKED
76404b540aSrobert #include <fcntl.h>
77404b540aSrobert #include <errno.h>
78404b540aSrobert #include <sys/stat.h>
79404b540aSrobert #endif
80404b540aSrobert
81404b540aSrobert #ifdef L_gcov
82404b540aSrobert #include "gcov-io.c"
83404b540aSrobert
84404b540aSrobert /* Chain of per-object gcov structures. */
85404b540aSrobert static struct gcov_info *gcov_list;
86404b540aSrobert
87404b540aSrobert /* A program checksum allows us to distinguish program data for an
88404b540aSrobert object file included in multiple programs. */
89404b540aSrobert static gcov_unsigned_t gcov_crc32;
90404b540aSrobert
91404b540aSrobert /* Size of the longest file name. */
92404b540aSrobert static size_t gcov_max_filename = 0;
93404b540aSrobert
94404b540aSrobert #ifdef TARGET_POSIX_IO
95404b540aSrobert /* Make sure path component of the given FILENAME exists, create
96404b540aSrobert missing directories. FILENAME must be writable.
97404b540aSrobert Returns zero on success, or -1 if an error occurred. */
98404b540aSrobert
99404b540aSrobert static int
create_file_directory(char * filename)100404b540aSrobert create_file_directory (char *filename)
101404b540aSrobert {
102404b540aSrobert char *s;
103404b540aSrobert
104404b540aSrobert for (s = filename + 1; *s != '\0'; s++)
105404b540aSrobert if (IS_DIR_SEPARATOR(*s))
106404b540aSrobert {
107404b540aSrobert char sep = *s;
108404b540aSrobert *s = '\0';
109404b540aSrobert
110404b540aSrobert /* Try to make directory if it doesn't already exist. */
111404b540aSrobert if (access (filename, F_OK) == -1
112404b540aSrobert && mkdir (filename, 0755) == -1
113404b540aSrobert /* The directory might have been made by another process. */
114404b540aSrobert && errno != EEXIST)
115404b540aSrobert {
116404b540aSrobert fprintf (stderr, "profiling:%s:Cannot create directory\n",
117404b540aSrobert filename);
118404b540aSrobert *s = sep;
119404b540aSrobert return -1;
120404b540aSrobert };
121404b540aSrobert
122404b540aSrobert *s = sep;
123404b540aSrobert };
124404b540aSrobert return 0;
125404b540aSrobert }
126404b540aSrobert #endif
127404b540aSrobert
128404b540aSrobert /* Check if VERSION of the info block PTR matches libgcov one.
129404b540aSrobert Return 1 on success, or zero in case of versions mismatch.
130404b540aSrobert If FILENAME is not NULL, its value used for reporting purposes
131404b540aSrobert instead of value from the info block. */
132404b540aSrobert
133404b540aSrobert static int
gcov_version(struct gcov_info * ptr,gcov_unsigned_t version,const char * filename)134404b540aSrobert gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
135404b540aSrobert const char *filename)
136404b540aSrobert {
137404b540aSrobert if (version != GCOV_VERSION)
138404b540aSrobert {
139404b540aSrobert char v[4], e[4];
140404b540aSrobert
141404b540aSrobert GCOV_UNSIGNED2STRING (v, version);
142404b540aSrobert GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
143404b540aSrobert
144404b540aSrobert fprintf (stderr,
145404b540aSrobert "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
146404b540aSrobert filename? filename : ptr->filename, e, v);
147404b540aSrobert return 0;
148404b540aSrobert }
149404b540aSrobert return 1;
150404b540aSrobert }
151404b540aSrobert
152404b540aSrobert /* Dump the coverage counts. We merge with existing counts when
153404b540aSrobert possible, to avoid growing the .da files ad infinitum. We use this
154404b540aSrobert program's checksum to make sure we only accumulate whole program
155404b540aSrobert statistics to the correct summary. An object file might be embedded
156404b540aSrobert in two separate programs, and we must keep the two program
157404b540aSrobert summaries separate. */
158404b540aSrobert
159404b540aSrobert static void
gcov_exit(void)160404b540aSrobert gcov_exit (void)
161404b540aSrobert {
162404b540aSrobert struct gcov_info *gi_ptr;
163404b540aSrobert struct gcov_summary this_program;
164404b540aSrobert struct gcov_summary all;
165404b540aSrobert struct gcov_ctr_summary *cs_ptr;
166404b540aSrobert const struct gcov_ctr_info *ci_ptr;
167404b540aSrobert unsigned t_ix;
168404b540aSrobert gcov_unsigned_t c_num;
169404b540aSrobert const char *gcov_prefix;
170404b540aSrobert int gcov_prefix_strip = 0;
171404b540aSrobert size_t prefix_length;
172404b540aSrobert char *gi_filename, *gi_filename_up;
173404b540aSrobert
174404b540aSrobert memset (&all, 0, sizeof (all));
175404b540aSrobert /* Find the totals for this execution. */
176404b540aSrobert memset (&this_program, 0, sizeof (this_program));
177404b540aSrobert for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
178404b540aSrobert {
179404b540aSrobert ci_ptr = gi_ptr->counts;
180404b540aSrobert for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
181404b540aSrobert {
182404b540aSrobert if (!((1 << t_ix) & gi_ptr->ctr_mask))
183404b540aSrobert continue;
184404b540aSrobert
185404b540aSrobert cs_ptr = &this_program.ctrs[t_ix];
186404b540aSrobert cs_ptr->num += ci_ptr->num;
187404b540aSrobert for (c_num = 0; c_num < ci_ptr->num; c_num++)
188404b540aSrobert {
189404b540aSrobert cs_ptr->sum_all += ci_ptr->values[c_num];
190404b540aSrobert if (cs_ptr->run_max < ci_ptr->values[c_num])
191404b540aSrobert cs_ptr->run_max = ci_ptr->values[c_num];
192404b540aSrobert }
193404b540aSrobert ci_ptr++;
194404b540aSrobert }
195404b540aSrobert }
196404b540aSrobert
197404b540aSrobert /* Get file name relocation prefix. Non-absolute values are ignored. */
198404b540aSrobert gcov_prefix = getenv("GCOV_PREFIX");
199404b540aSrobert if (gcov_prefix && IS_ABSOLUTE_PATH (gcov_prefix))
200404b540aSrobert {
201404b540aSrobert /* Check if the level of dirs to strip off specified. */
202404b540aSrobert char *tmp = getenv("GCOV_PREFIX_STRIP");
203404b540aSrobert if (tmp)
204404b540aSrobert {
205404b540aSrobert gcov_prefix_strip = atoi (tmp);
206404b540aSrobert /* Do not consider negative values. */
207404b540aSrobert if (gcov_prefix_strip < 0)
208404b540aSrobert gcov_prefix_strip = 0;
209404b540aSrobert }
210404b540aSrobert
211404b540aSrobert prefix_length = strlen(gcov_prefix);
212404b540aSrobert
213404b540aSrobert /* Remove an unnecessary trailing '/' */
214404b540aSrobert if (IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
215404b540aSrobert prefix_length--;
216404b540aSrobert }
217404b540aSrobert else
218404b540aSrobert prefix_length = 0;
219404b540aSrobert
220404b540aSrobert /* Allocate and initialize the filename scratch space. */
221404b540aSrobert gi_filename = (char *) alloca (prefix_length + gcov_max_filename + 1);
222404b540aSrobert if (prefix_length)
223404b540aSrobert memcpy (gi_filename, gcov_prefix, prefix_length);
224404b540aSrobert gi_filename_up = gi_filename + prefix_length;
225404b540aSrobert
226404b540aSrobert /* Now merge each file. */
227404b540aSrobert for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
228404b540aSrobert {
229404b540aSrobert struct gcov_summary this_object;
230404b540aSrobert struct gcov_summary object, program;
231404b540aSrobert gcov_type *values[GCOV_COUNTERS];
232404b540aSrobert const struct gcov_fn_info *fi_ptr;
233404b540aSrobert unsigned fi_stride;
234404b540aSrobert unsigned c_ix, f_ix, n_counts;
235404b540aSrobert struct gcov_ctr_summary *cs_obj, *cs_tobj, *cs_prg, *cs_tprg, *cs_all;
236404b540aSrobert int error = 0;
237404b540aSrobert gcov_unsigned_t tag, length;
238404b540aSrobert gcov_position_t summary_pos = 0;
239404b540aSrobert gcov_position_t eof_pos = 0;
240404b540aSrobert
241404b540aSrobert memset (&this_object, 0, sizeof (this_object));
242404b540aSrobert memset (&object, 0, sizeof (object));
243404b540aSrobert
244404b540aSrobert /* Build relocated filename, stripping off leading
245404b540aSrobert directories from the initial filename if requested. */
246404b540aSrobert if (gcov_prefix_strip > 0)
247404b540aSrobert {
248404b540aSrobert int level = 0;
249404b540aSrobert const char *fname = gi_ptr->filename;
250404b540aSrobert const char *s;
251404b540aSrobert
252404b540aSrobert /* Skip selected directory levels. */
253404b540aSrobert for (s = fname + 1; (*s != '\0') && (level < gcov_prefix_strip); s++)
254404b540aSrobert if (IS_DIR_SEPARATOR(*s))
255404b540aSrobert {
256404b540aSrobert fname = s;
257404b540aSrobert level++;
258404b540aSrobert };
259404b540aSrobert
260404b540aSrobert /* Update complete filename with stripped original. */
261*2a970957Smiod strlcpy (gi_filename_up, fname, gcov_max_filename + 1);
262404b540aSrobert }
263404b540aSrobert else
264*2a970957Smiod strlcpy (gi_filename_up, gi_ptr->filename, gcov_max_filename + 1);
265404b540aSrobert
266404b540aSrobert /* Totals for this object file. */
267404b540aSrobert ci_ptr = gi_ptr->counts;
268404b540aSrobert for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
269404b540aSrobert {
270404b540aSrobert if (!((1 << t_ix) & gi_ptr->ctr_mask))
271404b540aSrobert continue;
272404b540aSrobert
273404b540aSrobert cs_ptr = &this_object.ctrs[t_ix];
274404b540aSrobert cs_ptr->num += ci_ptr->num;
275404b540aSrobert for (c_num = 0; c_num < ci_ptr->num; c_num++)
276404b540aSrobert {
277404b540aSrobert cs_ptr->sum_all += ci_ptr->values[c_num];
278404b540aSrobert if (cs_ptr->run_max < ci_ptr->values[c_num])
279404b540aSrobert cs_ptr->run_max = ci_ptr->values[c_num];
280404b540aSrobert }
281404b540aSrobert
282404b540aSrobert ci_ptr++;
283404b540aSrobert }
284404b540aSrobert
285404b540aSrobert c_ix = 0;
286404b540aSrobert for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
287404b540aSrobert if ((1 << t_ix) & gi_ptr->ctr_mask)
288404b540aSrobert {
289404b540aSrobert values[c_ix] = gi_ptr->counts[c_ix].values;
290404b540aSrobert c_ix++;
291404b540aSrobert }
292404b540aSrobert
293404b540aSrobert /* Calculate the function_info stride. This depends on the
294404b540aSrobert number of counter types being measured. */
295404b540aSrobert fi_stride = sizeof (struct gcov_fn_info) + c_ix * sizeof (unsigned);
296404b540aSrobert if (__alignof__ (struct gcov_fn_info) > sizeof (unsigned))
297404b540aSrobert {
298404b540aSrobert fi_stride += __alignof__ (struct gcov_fn_info) - 1;
299404b540aSrobert fi_stride &= ~(__alignof__ (struct gcov_fn_info) - 1);
300404b540aSrobert }
301404b540aSrobert
302404b540aSrobert if (!gcov_open (gi_filename))
303404b540aSrobert {
304404b540aSrobert #ifdef TARGET_POSIX_IO
305404b540aSrobert /* Open failed likely due to missed directory.
306404b540aSrobert Create directory and retry to open file. */
307404b540aSrobert if (create_file_directory (gi_filename))
308404b540aSrobert {
309404b540aSrobert fprintf (stderr, "profiling:%s:Skip\n", gi_filename);
310404b540aSrobert continue;
311404b540aSrobert }
312404b540aSrobert #endif
313404b540aSrobert if (!gcov_open (gi_filename))
314404b540aSrobert {
315404b540aSrobert fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename);
316404b540aSrobert continue;
317404b540aSrobert }
318404b540aSrobert }
319404b540aSrobert
320404b540aSrobert tag = gcov_read_unsigned ();
321404b540aSrobert if (tag)
322404b540aSrobert {
323404b540aSrobert /* Merge data from file. */
324404b540aSrobert if (tag != GCOV_DATA_MAGIC)
325404b540aSrobert {
326404b540aSrobert fprintf (stderr, "profiling:%s:Not a gcov data file\n",
327404b540aSrobert gi_filename);
328404b540aSrobert goto read_fatal;
329404b540aSrobert }
330404b540aSrobert length = gcov_read_unsigned ();
331404b540aSrobert if (!gcov_version (gi_ptr, length, gi_filename))
332404b540aSrobert goto read_fatal;
333404b540aSrobert
334404b540aSrobert length = gcov_read_unsigned ();
335404b540aSrobert if (length != gi_ptr->stamp)
336404b540aSrobert /* Read from a different compilation. Overwrite the file. */
337404b540aSrobert goto rewrite;
338404b540aSrobert
339404b540aSrobert /* Merge execution counts for each function. */
340404b540aSrobert for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
341404b540aSrobert {
342404b540aSrobert fi_ptr = (const struct gcov_fn_info *)
343404b540aSrobert ((const char *) gi_ptr->functions + f_ix * fi_stride);
344404b540aSrobert tag = gcov_read_unsigned ();
345404b540aSrobert length = gcov_read_unsigned ();
346404b540aSrobert
347404b540aSrobert /* Check function. */
348404b540aSrobert if (tag != GCOV_TAG_FUNCTION
349404b540aSrobert || length != GCOV_TAG_FUNCTION_LENGTH
350404b540aSrobert || gcov_read_unsigned () != fi_ptr->ident
351404b540aSrobert || gcov_read_unsigned () != fi_ptr->checksum)
352404b540aSrobert {
353404b540aSrobert read_mismatch:;
354404b540aSrobert fprintf (stderr, "profiling:%s:Merge mismatch for %s\n",
355404b540aSrobert gi_filename,
356404b540aSrobert f_ix + 1 ? "function" : "summaries");
357404b540aSrobert goto read_fatal;
358404b540aSrobert }
359404b540aSrobert
360404b540aSrobert c_ix = 0;
361404b540aSrobert for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
362404b540aSrobert {
363404b540aSrobert gcov_merge_fn merge;
364404b540aSrobert
365404b540aSrobert if (!((1 << t_ix) & gi_ptr->ctr_mask))
366404b540aSrobert continue;
367404b540aSrobert
368404b540aSrobert n_counts = fi_ptr->n_ctrs[c_ix];
369404b540aSrobert merge = gi_ptr->counts[c_ix].merge;
370404b540aSrobert
371404b540aSrobert tag = gcov_read_unsigned ();
372404b540aSrobert length = gcov_read_unsigned ();
373404b540aSrobert if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
374404b540aSrobert || length != GCOV_TAG_COUNTER_LENGTH (n_counts))
375404b540aSrobert goto read_mismatch;
376404b540aSrobert (*merge) (values[c_ix], n_counts);
377404b540aSrobert values[c_ix] += n_counts;
378404b540aSrobert c_ix++;
379404b540aSrobert }
380404b540aSrobert if ((error = gcov_is_error ()))
381404b540aSrobert goto read_error;
382404b540aSrobert }
383404b540aSrobert
384404b540aSrobert f_ix = ~0u;
385404b540aSrobert /* Check program & object summary */
386404b540aSrobert while (1)
387404b540aSrobert {
388404b540aSrobert int is_program;
389404b540aSrobert
390404b540aSrobert eof_pos = gcov_position ();
391404b540aSrobert tag = gcov_read_unsigned ();
392404b540aSrobert if (!tag)
393404b540aSrobert break;
394404b540aSrobert
395404b540aSrobert length = gcov_read_unsigned ();
396404b540aSrobert is_program = tag == GCOV_TAG_PROGRAM_SUMMARY;
397404b540aSrobert if (length != GCOV_TAG_SUMMARY_LENGTH
398404b540aSrobert || (!is_program && tag != GCOV_TAG_OBJECT_SUMMARY))
399404b540aSrobert goto read_mismatch;
400404b540aSrobert gcov_read_summary (is_program ? &program : &object);
401404b540aSrobert if ((error = gcov_is_error ()))
402404b540aSrobert goto read_error;
403404b540aSrobert if (is_program && program.checksum == gcov_crc32)
404404b540aSrobert {
405404b540aSrobert summary_pos = eof_pos;
406404b540aSrobert goto rewrite;
407404b540aSrobert }
408404b540aSrobert }
409404b540aSrobert }
410404b540aSrobert goto rewrite;
411404b540aSrobert
412404b540aSrobert read_error:;
413404b540aSrobert fprintf (stderr, error < 0 ? "profiling:%s:Overflow merging\n"
414404b540aSrobert : "profiling:%s:Error merging\n", gi_filename);
415404b540aSrobert
416404b540aSrobert read_fatal:;
417404b540aSrobert gcov_close ();
418404b540aSrobert continue;
419404b540aSrobert
420404b540aSrobert rewrite:;
421404b540aSrobert gcov_rewrite ();
422404b540aSrobert if (!summary_pos)
423404b540aSrobert memset (&program, 0, sizeof (program));
424404b540aSrobert
425404b540aSrobert /* Merge the summaries. */
426404b540aSrobert f_ix = ~0u;
427404b540aSrobert for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
428404b540aSrobert {
429404b540aSrobert cs_obj = &object.ctrs[t_ix];
430404b540aSrobert cs_tobj = &this_object.ctrs[t_ix];
431404b540aSrobert cs_prg = &program.ctrs[t_ix];
432404b540aSrobert cs_tprg = &this_program.ctrs[t_ix];
433404b540aSrobert cs_all = &all.ctrs[t_ix];
434404b540aSrobert
435404b540aSrobert if ((1 << t_ix) & gi_ptr->ctr_mask)
436404b540aSrobert {
437404b540aSrobert if (!cs_obj->runs++)
438404b540aSrobert cs_obj->num = cs_tobj->num;
439404b540aSrobert else if (cs_obj->num != cs_tobj->num)
440404b540aSrobert goto read_mismatch;
441404b540aSrobert cs_obj->sum_all += cs_tobj->sum_all;
442404b540aSrobert if (cs_obj->run_max < cs_tobj->run_max)
443404b540aSrobert cs_obj->run_max = cs_tobj->run_max;
444404b540aSrobert cs_obj->sum_max += cs_tobj->run_max;
445404b540aSrobert
446404b540aSrobert if (!cs_prg->runs++)
447404b540aSrobert cs_prg->num = cs_tprg->num;
448404b540aSrobert else if (cs_prg->num != cs_tprg->num)
449404b540aSrobert goto read_mismatch;
450404b540aSrobert cs_prg->sum_all += cs_tprg->sum_all;
451404b540aSrobert if (cs_prg->run_max < cs_tprg->run_max)
452404b540aSrobert cs_prg->run_max = cs_tprg->run_max;
453404b540aSrobert cs_prg->sum_max += cs_tprg->run_max;
454404b540aSrobert }
455404b540aSrobert else if (cs_obj->num || cs_prg->num)
456404b540aSrobert goto read_mismatch;
457404b540aSrobert
458404b540aSrobert if (!cs_all->runs && cs_prg->runs)
459404b540aSrobert memcpy (cs_all, cs_prg, sizeof (*cs_all));
460404b540aSrobert else if (!all.checksum
461404b540aSrobert && (!GCOV_LOCKED || cs_all->runs == cs_prg->runs)
462404b540aSrobert && memcmp (cs_all, cs_prg, sizeof (*cs_all)))
463404b540aSrobert {
464404b540aSrobert fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s",
465404b540aSrobert gi_filename, GCOV_LOCKED
466404b540aSrobert ? "" : " or concurrent update without locking support");
467404b540aSrobert all.checksum = ~0u;
468404b540aSrobert }
469404b540aSrobert }
470404b540aSrobert
471404b540aSrobert c_ix = 0;
472404b540aSrobert for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
473404b540aSrobert if ((1 << t_ix) & gi_ptr->ctr_mask)
474404b540aSrobert {
475404b540aSrobert values[c_ix] = gi_ptr->counts[c_ix].values;
476404b540aSrobert c_ix++;
477404b540aSrobert }
478404b540aSrobert
479404b540aSrobert program.checksum = gcov_crc32;
480404b540aSrobert
481404b540aSrobert /* Write out the data. */
482404b540aSrobert gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
483404b540aSrobert gcov_write_unsigned (gi_ptr->stamp);
484404b540aSrobert
485404b540aSrobert /* Write execution counts for each function. */
486404b540aSrobert for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
487404b540aSrobert {
488404b540aSrobert fi_ptr = (const struct gcov_fn_info *)
489404b540aSrobert ((const char *) gi_ptr->functions + f_ix * fi_stride);
490404b540aSrobert
491404b540aSrobert /* Announce function. */
492404b540aSrobert gcov_write_tag_length (GCOV_TAG_FUNCTION, GCOV_TAG_FUNCTION_LENGTH);
493404b540aSrobert gcov_write_unsigned (fi_ptr->ident);
494404b540aSrobert gcov_write_unsigned (fi_ptr->checksum);
495404b540aSrobert
496404b540aSrobert c_ix = 0;
497404b540aSrobert for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
498404b540aSrobert {
499404b540aSrobert gcov_type *c_ptr;
500404b540aSrobert
501404b540aSrobert if (!((1 << t_ix) & gi_ptr->ctr_mask))
502404b540aSrobert continue;
503404b540aSrobert
504404b540aSrobert n_counts = fi_ptr->n_ctrs[c_ix];
505404b540aSrobert
506404b540aSrobert gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
507404b540aSrobert GCOV_TAG_COUNTER_LENGTH (n_counts));
508404b540aSrobert c_ptr = values[c_ix];
509404b540aSrobert while (n_counts--)
510404b540aSrobert gcov_write_counter (*c_ptr++);
511404b540aSrobert
512404b540aSrobert values[c_ix] = c_ptr;
513404b540aSrobert c_ix++;
514404b540aSrobert }
515404b540aSrobert }
516404b540aSrobert
517404b540aSrobert /* Object file summary. */
518404b540aSrobert gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, &object);
519404b540aSrobert
520404b540aSrobert /* Generate whole program statistics. */
521404b540aSrobert if (eof_pos)
522404b540aSrobert gcov_seek (eof_pos);
523404b540aSrobert gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &program);
524404b540aSrobert if (!summary_pos)
525404b540aSrobert gcov_write_unsigned (0);
526404b540aSrobert if ((error = gcov_close ()))
527404b540aSrobert fprintf (stderr, error < 0 ?
528404b540aSrobert "profiling:%s:Overflow writing\n" :
529404b540aSrobert "profiling:%s:Error writing\n",
530404b540aSrobert gi_filename);
531404b540aSrobert }
532404b540aSrobert }
533404b540aSrobert
534404b540aSrobert /* Add a new object file onto the bb chain. Invoked automatically
535404b540aSrobert when running an object file's global ctors. */
536404b540aSrobert
537404b540aSrobert void
__gcov_init(struct gcov_info * info)538404b540aSrobert __gcov_init (struct gcov_info *info)
539404b540aSrobert {
540404b540aSrobert if (!info->version)
541404b540aSrobert return;
542404b540aSrobert if (gcov_version (info, info->version, 0))
543404b540aSrobert {
544404b540aSrobert const char *ptr = info->filename;
545404b540aSrobert gcov_unsigned_t crc32 = gcov_crc32;
546404b540aSrobert size_t filename_length = strlen(info->filename);
547404b540aSrobert
548404b540aSrobert /* Refresh the longest file name information */
549404b540aSrobert if (filename_length > gcov_max_filename)
550404b540aSrobert gcov_max_filename = filename_length;
551404b540aSrobert
552404b540aSrobert do
553404b540aSrobert {
554404b540aSrobert unsigned ix;
555404b540aSrobert gcov_unsigned_t value = *ptr << 24;
556404b540aSrobert
557404b540aSrobert for (ix = 8; ix--; value <<= 1)
558404b540aSrobert {
559404b540aSrobert gcov_unsigned_t feedback;
560404b540aSrobert
561404b540aSrobert feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
562404b540aSrobert crc32 <<= 1;
563404b540aSrobert crc32 ^= feedback;
564404b540aSrobert }
565404b540aSrobert }
566404b540aSrobert while (*ptr++);
567404b540aSrobert
568404b540aSrobert gcov_crc32 = crc32;
569404b540aSrobert
570404b540aSrobert if (!gcov_list)
571404b540aSrobert atexit (gcov_exit);
572404b540aSrobert
573404b540aSrobert info->next = gcov_list;
574404b540aSrobert gcov_list = info;
575404b540aSrobert }
576404b540aSrobert info->version = 0;
577404b540aSrobert }
578404b540aSrobert
579404b540aSrobert /* Called before fork or exec - write out profile information gathered so
580404b540aSrobert far and reset it to zero. This avoids duplication or loss of the
581404b540aSrobert profile information gathered so far. */
582404b540aSrobert
583404b540aSrobert void
__gcov_flush(void)584404b540aSrobert __gcov_flush (void)
585404b540aSrobert {
586404b540aSrobert const struct gcov_info *gi_ptr;
587404b540aSrobert
588404b540aSrobert gcov_exit ();
589404b540aSrobert for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
590404b540aSrobert {
591404b540aSrobert unsigned t_ix;
592404b540aSrobert const struct gcov_ctr_info *ci_ptr;
593404b540aSrobert
594404b540aSrobert for (t_ix = 0, ci_ptr = gi_ptr->counts; t_ix != GCOV_COUNTERS; t_ix++)
595404b540aSrobert if ((1 << t_ix) & gi_ptr->ctr_mask)
596404b540aSrobert {
597404b540aSrobert memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
598404b540aSrobert ci_ptr++;
599404b540aSrobert }
600404b540aSrobert }
601404b540aSrobert }
602404b540aSrobert
603404b540aSrobert #endif /* L_gcov */
604404b540aSrobert
605404b540aSrobert #ifdef L_gcov_merge_add
606404b540aSrobert /* The profile merging function that just adds the counters. It is given
607404b540aSrobert an array COUNTERS of N_COUNTERS old counters and it reads the same number
608404b540aSrobert of counters from the gcov file. */
609404b540aSrobert void
__gcov_merge_add(gcov_type * counters,unsigned n_counters)610404b540aSrobert __gcov_merge_add (gcov_type *counters, unsigned n_counters)
611404b540aSrobert {
612404b540aSrobert for (; n_counters; counters++, n_counters--)
613404b540aSrobert *counters += gcov_read_counter ();
614404b540aSrobert }
615404b540aSrobert #endif /* L_gcov_merge_add */
616404b540aSrobert
617404b540aSrobert #ifdef L_gcov_merge_single
618404b540aSrobert /* The profile merging function for choosing the most common value.
619404b540aSrobert It is given an array COUNTERS of N_COUNTERS old counters and it
620404b540aSrobert reads the same number of counters from the gcov file. The counters
621404b540aSrobert are split into 3-tuples where the members of the tuple have
622404b540aSrobert meanings:
623404b540aSrobert
624404b540aSrobert -- the stored candidate on the most common value of the measured entity
625404b540aSrobert -- counter
626404b540aSrobert -- total number of evaluations of the value */
627404b540aSrobert void
__gcov_merge_single(gcov_type * counters,unsigned n_counters)628404b540aSrobert __gcov_merge_single (gcov_type *counters, unsigned n_counters)
629404b540aSrobert {
630404b540aSrobert unsigned i, n_measures;
631404b540aSrobert gcov_type value, counter, all;
632404b540aSrobert
633404b540aSrobert gcc_assert (!(n_counters % 3));
634404b540aSrobert n_measures = n_counters / 3;
635404b540aSrobert for (i = 0; i < n_measures; i++, counters += 3)
636404b540aSrobert {
637404b540aSrobert value = gcov_read_counter ();
638404b540aSrobert counter = gcov_read_counter ();
639404b540aSrobert all = gcov_read_counter ();
640404b540aSrobert
641404b540aSrobert if (counters[0] == value)
642404b540aSrobert counters[1] += counter;
643404b540aSrobert else if (counter > counters[1])
644404b540aSrobert {
645404b540aSrobert counters[0] = value;
646404b540aSrobert counters[1] = counter - counters[1];
647404b540aSrobert }
648404b540aSrobert else
649404b540aSrobert counters[1] -= counter;
650404b540aSrobert counters[2] += all;
651404b540aSrobert }
652404b540aSrobert }
653404b540aSrobert #endif /* L_gcov_merge_single */
654404b540aSrobert
655404b540aSrobert #ifdef L_gcov_merge_delta
656404b540aSrobert /* The profile merging function for choosing the most common
657404b540aSrobert difference between two consecutive evaluations of the value. It is
658404b540aSrobert given an array COUNTERS of N_COUNTERS old counters and it reads the
659404b540aSrobert same number of counters from the gcov file. The counters are split
660404b540aSrobert into 4-tuples where the members of the tuple have meanings:
661404b540aSrobert
662404b540aSrobert -- the last value of the measured entity
663404b540aSrobert -- the stored candidate on the most common difference
664404b540aSrobert -- counter
665404b540aSrobert -- total number of evaluations of the value */
666404b540aSrobert void
__gcov_merge_delta(gcov_type * counters,unsigned n_counters)667404b540aSrobert __gcov_merge_delta (gcov_type *counters, unsigned n_counters)
668404b540aSrobert {
669404b540aSrobert unsigned i, n_measures;
670404b540aSrobert gcov_type last, value, counter, all;
671404b540aSrobert
672404b540aSrobert gcc_assert (!(n_counters % 4));
673404b540aSrobert n_measures = n_counters / 4;
674404b540aSrobert for (i = 0; i < n_measures; i++, counters += 4)
675404b540aSrobert {
676404b540aSrobert last = gcov_read_counter ();
677404b540aSrobert value = gcov_read_counter ();
678404b540aSrobert counter = gcov_read_counter ();
679404b540aSrobert all = gcov_read_counter ();
680404b540aSrobert
681404b540aSrobert if (counters[1] == value)
682404b540aSrobert counters[2] += counter;
683404b540aSrobert else if (counter > counters[2])
684404b540aSrobert {
685404b540aSrobert counters[1] = value;
686404b540aSrobert counters[2] = counter - counters[2];
687404b540aSrobert }
688404b540aSrobert else
689404b540aSrobert counters[2] -= counter;
690404b540aSrobert counters[3] += all;
691404b540aSrobert }
692404b540aSrobert }
693404b540aSrobert #endif /* L_gcov_merge_delta */
694404b540aSrobert
695404b540aSrobert #ifdef L_gcov_interval_profiler
696404b540aSrobert /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
697404b540aSrobert corresponding counter in COUNTERS. If the VALUE is above or below
698404b540aSrobert the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
699404b540aSrobert instead. */
700404b540aSrobert
701404b540aSrobert void
__gcov_interval_profiler(gcov_type * counters,gcov_type value,int start,unsigned steps)702404b540aSrobert __gcov_interval_profiler (gcov_type *counters, gcov_type value,
703404b540aSrobert int start, unsigned steps)
704404b540aSrobert {
705404b540aSrobert gcov_type delta = value - start;
706404b540aSrobert if (delta < 0)
707404b540aSrobert counters[steps + 1]++;
708404b540aSrobert else if (delta >= steps)
709404b540aSrobert counters[steps]++;
710404b540aSrobert else
711404b540aSrobert counters[delta]++;
712404b540aSrobert }
713404b540aSrobert #endif
714404b540aSrobert
715404b540aSrobert #ifdef L_gcov_pow2_profiler
716404b540aSrobert /* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
717404b540aSrobert COUNTERS[0] is incremented. */
718404b540aSrobert
719404b540aSrobert void
__gcov_pow2_profiler(gcov_type * counters,gcov_type value)720404b540aSrobert __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
721404b540aSrobert {
722404b540aSrobert if (value & (value - 1))
723404b540aSrobert counters[0]++;
724404b540aSrobert else
725404b540aSrobert counters[1]++;
726404b540aSrobert }
727404b540aSrobert #endif
728404b540aSrobert
729404b540aSrobert #ifdef L_gcov_one_value_profiler
730404b540aSrobert /* Tries to determine the most common value among its inputs. Checks if the
731404b540aSrobert value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1]
732404b540aSrobert is incremented. If this is not the case and COUNTERS[1] is not zero,
733404b540aSrobert COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and
734404b540aSrobert VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this
735404b540aSrobert function is called more than 50% of the time with one value, this value
736404b540aSrobert will be in COUNTERS[0] in the end.
737404b540aSrobert
738404b540aSrobert In any case, COUNTERS[2] is incremented. */
739404b540aSrobert
740404b540aSrobert void
__gcov_one_value_profiler(gcov_type * counters,gcov_type value)741404b540aSrobert __gcov_one_value_profiler (gcov_type *counters, gcov_type value)
742404b540aSrobert {
743404b540aSrobert if (value == counters[0])
744404b540aSrobert counters[1]++;
745404b540aSrobert else if (counters[1] == 0)
746404b540aSrobert {
747404b540aSrobert counters[1] = 1;
748404b540aSrobert counters[0] = value;
749404b540aSrobert }
750404b540aSrobert else
751404b540aSrobert counters[1]--;
752404b540aSrobert counters[2]++;
753404b540aSrobert }
754404b540aSrobert #endif
755404b540aSrobert
756404b540aSrobert #ifdef L_gcov_fork
757404b540aSrobert /* A wrapper for the fork function. Flushes the accumulated profiling data, so
758404b540aSrobert that they are not counted twice. */
759404b540aSrobert
760404b540aSrobert pid_t
__gcov_fork(void)761404b540aSrobert __gcov_fork (void)
762404b540aSrobert {
763404b540aSrobert __gcov_flush ();
764404b540aSrobert return fork ();
765404b540aSrobert }
766404b540aSrobert #endif
767404b540aSrobert
768404b540aSrobert #ifdef L_gcov_execl
769404b540aSrobert /* A wrapper for the execl function. Flushes the accumulated profiling data, so
770404b540aSrobert that they are not lost. */
771404b540aSrobert
772404b540aSrobert int
__gcov_execl(const char * path,const char * arg,...)773404b540aSrobert __gcov_execl (const char *path, const char *arg, ...)
774404b540aSrobert {
775404b540aSrobert va_list ap, aq;
776404b540aSrobert unsigned i, length;
777404b540aSrobert char **args;
778404b540aSrobert
779404b540aSrobert __gcov_flush ();
780404b540aSrobert
781404b540aSrobert va_start (ap, arg);
782404b540aSrobert va_copy (aq, ap);
783404b540aSrobert
784404b540aSrobert length = 2;
785404b540aSrobert while (va_arg (ap, char *))
786404b540aSrobert length++;
787404b540aSrobert va_end (ap);
788404b540aSrobert
789404b540aSrobert args = (char **) alloca (length * sizeof (void *));
790404b540aSrobert args[0] = (char *) arg;
791404b540aSrobert for (i = 1; i < length; i++)
792404b540aSrobert args[i] = va_arg (aq, char *);
793404b540aSrobert va_end (aq);
794404b540aSrobert
795404b540aSrobert return execv (path, args);
796404b540aSrobert }
797404b540aSrobert #endif
798404b540aSrobert
799404b540aSrobert #ifdef L_gcov_execlp
800404b540aSrobert /* A wrapper for the execlp function. Flushes the accumulated profiling data, so
801404b540aSrobert that they are not lost. */
802404b540aSrobert
803404b540aSrobert int
__gcov_execlp(const char * path,const char * arg,...)804404b540aSrobert __gcov_execlp (const char *path, const char *arg, ...)
805404b540aSrobert {
806404b540aSrobert va_list ap, aq;
807404b540aSrobert unsigned i, length;
808404b540aSrobert char **args;
809404b540aSrobert
810404b540aSrobert __gcov_flush ();
811404b540aSrobert
812404b540aSrobert va_start (ap, arg);
813404b540aSrobert va_copy (aq, ap);
814404b540aSrobert
815404b540aSrobert length = 2;
816404b540aSrobert while (va_arg (ap, char *))
817404b540aSrobert length++;
818404b540aSrobert va_end (ap);
819404b540aSrobert
820404b540aSrobert args = (char **) alloca (length * sizeof (void *));
821404b540aSrobert args[0] = (char *) arg;
822404b540aSrobert for (i = 1; i < length; i++)
823404b540aSrobert args[i] = va_arg (aq, char *);
824404b540aSrobert va_end (aq);
825404b540aSrobert
826404b540aSrobert return execvp (path, args);
827404b540aSrobert }
828404b540aSrobert #endif
829404b540aSrobert
830404b540aSrobert #ifdef L_gcov_execle
831404b540aSrobert /* A wrapper for the execle function. Flushes the accumulated profiling data, so
832404b540aSrobert that they are not lost. */
833404b540aSrobert
834404b540aSrobert int
__gcov_execle(const char * path,const char * arg,...)835404b540aSrobert __gcov_execle (const char *path, const char *arg, ...)
836404b540aSrobert {
837404b540aSrobert va_list ap, aq;
838404b540aSrobert unsigned i, length;
839404b540aSrobert char **args;
840404b540aSrobert char **envp;
841404b540aSrobert
842404b540aSrobert __gcov_flush ();
843404b540aSrobert
844404b540aSrobert va_start (ap, arg);
845404b540aSrobert va_copy (aq, ap);
846404b540aSrobert
847404b540aSrobert length = 2;
848404b540aSrobert while (va_arg (ap, char *))
849404b540aSrobert length++;
850404b540aSrobert va_end (ap);
851404b540aSrobert
852404b540aSrobert args = (char **) alloca (length * sizeof (void *));
853404b540aSrobert args[0] = (char *) arg;
854404b540aSrobert for (i = 1; i < length; i++)
855404b540aSrobert args[i] = va_arg (aq, char *);
856404b540aSrobert envp = va_arg (aq, char **);
857404b540aSrobert va_end (aq);
858404b540aSrobert
859404b540aSrobert return execve (path, args, envp);
860404b540aSrobert }
861404b540aSrobert #endif
862404b540aSrobert
863404b540aSrobert #ifdef L_gcov_execv
864404b540aSrobert /* A wrapper for the execv function. Flushes the accumulated profiling data, so
865404b540aSrobert that they are not lost. */
866404b540aSrobert
867404b540aSrobert int
__gcov_execv(const char * path,char * const argv[])868404b540aSrobert __gcov_execv (const char *path, char *const argv[])
869404b540aSrobert {
870404b540aSrobert __gcov_flush ();
871404b540aSrobert return execv (path, argv);
872404b540aSrobert }
873404b540aSrobert #endif
874404b540aSrobert
875404b540aSrobert #ifdef L_gcov_execvp
876404b540aSrobert /* A wrapper for the execvp function. Flushes the accumulated profiling data, so
877404b540aSrobert that they are not lost. */
878404b540aSrobert
879404b540aSrobert int
__gcov_execvp(const char * path,char * const argv[])880404b540aSrobert __gcov_execvp (const char *path, char *const argv[])
881404b540aSrobert {
882404b540aSrobert __gcov_flush ();
883404b540aSrobert return execvp (path, argv);
884404b540aSrobert }
885404b540aSrobert #endif
886404b540aSrobert
887404b540aSrobert #ifdef L_gcov_execve
888404b540aSrobert /* A wrapper for the execve function. Flushes the accumulated profiling data, so
889404b540aSrobert that they are not lost. */
890404b540aSrobert
891404b540aSrobert int
__gcov_execve(const char * path,char * const argv[],char * const envp[])892404b540aSrobert __gcov_execve (const char *path, char *const argv[], char *const envp[])
893404b540aSrobert {
894404b540aSrobert __gcov_flush ();
895404b540aSrobert return execve (path, argv, envp);
896404b540aSrobert }
897404b540aSrobert #endif
898404b540aSrobert #endif /* inhibit_libc */
899