]> gcc.gnu.org Git - gcc.git/blame - gcc/libgcov.c
pa.c (compute_frame_size): Don't assume PREFERRED_STACK_BOUNDARY is 8 * STACK_BOUNDARY.
[gcc.git] / gcc / libgcov.c
CommitLineData
23af32e6
NS
1/* Routines required for instrumenting a program. */
2/* Compile this one with gcc. */
3/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
4 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
5
6This file is part of GCC.
7
8GCC is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13In addition to the permissions in the GNU General Public License, the
14Free Software Foundation gives you unlimited permission to link the
15compiled version of this file into combinations with other programs,
16and to distribute those combinations without any restriction coming
17from the use of this file. (The General Public License restrictions
18do apply in other respects; for example, they cover modification of
19the file, and distribution when not linked into a combine
20executable.)
21
22GCC is distributed in the hope that it will be useful, but WITHOUT ANY
23WARRANTY; without even the implied warranty of MERCHANTABILITY or
24FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25for more details.
26
27You should have received a copy of the GNU General Public License
28along with GCC; see the file COPYING. If not, write to the Free
29Software Foundation, 59 Temple Place - Suite 330, Boston, MA
3002111-1307, USA. */
31
32/* It is incorrect to include config.h here, because this file is being
33 compiled for the target, and hence definitions concerning only the host
34 do not apply. */
35
36#include "tconfig.h"
37#include "tsystem.h"
38#include "coretypes.h"
39#include "tm.h"
40
41#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
42#include <stdio.h>
43
44#include "gcov-io.h"
45#include <string.h>
46#if defined (TARGET_HAS_F_SETLKW)
47#include <fcntl.h>
48#include <errno.h>
49#endif
50
51/* Chain of per-object gcov structures. */
52static struct gcov_info *gcov_list;
53
54/* A program checksum allows us to distinguish program data for an
55 object file included in multiple programs. */
56static unsigned gcov_crc32;
57
58static void
59gcov_version_mismatch (struct gcov_info *ptr, unsigned version)
60{
61 unsigned expected = GCOV_VERSION;
62 unsigned ix;
63 char e[4], v[4];
64
65 for (ix = 4; ix--; expected >>= 8, version >>= 8)
66 {
67 e[ix] = expected;
68 v[ix] = version;
69 }
70
71 fprintf (stderr,
72 "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
73 ptr->filename, e, v);
74}
75
76/* Dump the coverage counts. We merge with existing counts when
77 possible, to avoid growing the .da files ad infinitum. We use this
78 program's checksum to make sure we only accumulate whole program
79 statistics to the correct summary. An object file might be embedded
80 in two separate programs, and we must keep the two program
81 summaries separate. */
82
83static void
84gcov_exit (void)
85{
86 struct gcov_info *ptr;
87 unsigned ix, jx;
88 struct gcov_summary program;
89 gcov_type program_max_one = 0;
90 gcov_type program_max_sum = 0;
91 gcov_type program_sum = 0;
92 unsigned program_arcs = 0;
93
94#if defined (TARGET_HAS_F_SETLKW)
95 struct flock s_flock;
96
97 s_flock.l_type = F_WRLCK;
98 s_flock.l_whence = SEEK_SET;
99 s_flock.l_start = 0;
100 s_flock.l_len = 0; /* Until EOF. */
101 s_flock.l_pid = getpid ();
102#endif
103
104 memset (&program, 0, sizeof (program));
105 program.checksum = gcov_crc32;
106
107 for (ptr = gcov_list; ptr; ptr = ptr->next)
108 {
109 FILE *da_file;
110 struct gcov_summary object;
111 struct gcov_summary local_prg;
112 int merging = 0;
113 long base;
114 const struct function_info *fn_info;
115 gcov_type *count_ptr;
116 gcov_type object_max_one = 0;
117
118 ptr->wkspc = 0;
119 if (!ptr->filename)
120 continue;
121
122 for (ix = ptr->n_arc_counts, count_ptr = ptr->arc_counts; ix--;)
123 {
124 gcov_type count = *count_ptr++;
125
126 if (count > object_max_one)
127 object_max_one = count;
128 }
129 if (object_max_one > program_max_one)
130 program_max_one = object_max_one;
131
132 memset (&local_prg, 0, sizeof (local_prg));
133 memset (&object, 0, sizeof (object));
134
135 /* Open for modification */
136 if ((da_file = fopen (ptr->filename, "r+b")))
137 merging = 1;
138 else if ((da_file = fopen (ptr->filename, "w+b")))
139 ;
140 else
141 {
142 fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
143 ptr->filename = 0;
144 continue;
145 }
146
147#if defined (TARGET_HAS_F_SETLKW)
148 /* After a fork, another process might try to read and/or write
149 the same file simultaneously. So if we can, lock the file to
150 avoid race conditions. */
151 while (fcntl (fileno (da_file), F_SETLKW, &s_flock)
152 && errno == EINTR)
153 continue;
154#endif
155 if (merging)
156 {
157 /* Merge data from file. */
158 unsigned tag, length;
159
160 if (gcov_read_unsigned (da_file, &tag) || tag != GCOV_DATA_MAGIC)
161 {
162 fprintf (stderr, "profiling:%s:Not a gcov data file\n",
163 ptr->filename);
164 read_fatal:;
165 fclose (da_file);
166 ptr->filename = 0;
167 continue;
168 }
169 if (gcov_read_unsigned (da_file, &length) || length != GCOV_VERSION)
170 {
171 gcov_version_mismatch (ptr, length);
172 goto read_fatal;
173 }
174
175 /* Merge execution counts for each function. */
176 count_ptr = ptr->arc_counts;
177 for (ix = ptr->n_functions, fn_info = ptr->functions;
178 ix--; fn_info++)
179 {
180 if (gcov_read_unsigned (da_file, &tag)
181 || gcov_read_unsigned (da_file, &length))
182 {
183 read_error:;
184 fprintf (stderr, "profiling:%s:Error merging\n",
185 ptr->filename);
186 goto read_fatal;
187 }
188
189 /* Check function */
190 if (tag != GCOV_TAG_FUNCTION)
191 {
192 read_mismatch:;
193 fprintf (stderr, "profiling:%s:Merge mismatch at %s\n",
194 ptr->filename, fn_info->name);
195 goto read_fatal;
196 }
197 {
198 unsigned flength, checksum;
199
200 if (gcov_read_unsigned (da_file, &flength)
201 || gcov_skip_string (da_file, flength)
202 || gcov_read_unsigned (da_file, &checksum))
203 goto read_error;
204 if (flength != strlen (fn_info->name)
205 || checksum != fn_info->checksum)
206 goto read_mismatch;
207 }
208 /* Check arc counts */
209 if (gcov_read_unsigned (da_file, &tag)
210 || gcov_read_unsigned (da_file, &length))
211 goto read_error;
212 if (tag != GCOV_TAG_ARC_COUNTS
213 || length / 8 != fn_info->n_arc_counts)
214 goto read_mismatch;
215 {
216 gcov_type count;
217
218 for (jx = fn_info->n_arc_counts; jx--; count_ptr++)
219 if (gcov_read_counter (da_file, &count))
220 goto read_error;
221 else
222 *count_ptr += count;
223 }
224 }
225
226 /* Check object summary */
227 if (gcov_read_unsigned (da_file, &tag)
228 || gcov_read_unsigned (da_file, &length))
229 goto read_error;
230 if (tag != GCOV_TAG_OBJECT_SUMMARY)
231 goto read_mismatch;
232 if (gcov_read_summary (da_file, &object))
233 goto read_error;
234
235 /* Check program summary */
236 while (1)
237 {
238 long base = ftell (da_file);
239
240 if (gcov_read_unsigned (da_file, &tag)
241 || gcov_read_unsigned (da_file, &length))
242 {
243 if (feof (da_file))
244 break;
245 goto read_error;
246 }
247 if (tag != GCOV_TAG_PROGRAM_SUMMARY
248 && tag != GCOV_TAG_PLACEHOLDER_SUMMARY
249 && tag != GCOV_TAG_INCORRECT_SUMMARY)
250 goto read_mismatch;
251 if (gcov_read_summary (da_file, &local_prg))
252 goto read_error;
253 if (local_prg.checksum != program.checksum)
254 continue;
255 if (tag == GCOV_TAG_PLACEHOLDER_SUMMARY)
256 {
257 fprintf (stderr,
258 "profiling:%s:Concurrent race detected\n",
259 ptr->filename);
260 goto read_fatal;
261 }
262 merging = -1;
263 if (tag != GCOV_TAG_PROGRAM_SUMMARY)
264 break;
265
266 if (program.runs
267 && memcmp (&program, &local_prg, sizeof (program)))
268 {
269 fprintf (stderr, "profiling:%s:Invocation mismatch\n",
270 ptr->filename);
271 local_prg.runs = 0;
272 }
273 else
274 memcpy (&program, &local_prg, sizeof (program));
275 ptr->wkspc = base;
276 break;
277 }
278 fseek (da_file, 0, SEEK_SET);
279 }
280
281 object.runs++;
282 object.arcs = ptr->n_arc_counts;
283 object.arc_sum = 0;
284 if (object.arc_max_one < object_max_one)
285 object.arc_max_one = object_max_one;
286 object.arc_sum_max += object_max_one;
287
288 /* Write out the data. */
289 if (/* magic */
290 gcov_write_unsigned (da_file, GCOV_DATA_MAGIC)
291 /* version number */
292 || gcov_write_unsigned (da_file, GCOV_VERSION))
293 {
294 write_error:;
295 fclose (da_file);
296 fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
297 ptr->filename = 0;
298 continue;
299 }
300
301 /* Write execution counts for each function. */
302 count_ptr = ptr->arc_counts;
303 for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++)
304 {
305 /* Announce function. */
306 if (gcov_write_unsigned (da_file, GCOV_TAG_FUNCTION)
307 || !(base = gcov_reserve_length (da_file))
308 /* function name */
309 || gcov_write_string (da_file, fn_info->name,
310 strlen (fn_info->name))
311 /* function checksum */
312 || gcov_write_unsigned (da_file, fn_info->checksum)
313 || gcov_write_length (da_file, base))
314 goto write_error;
315
316 /* arc counts. */
317 if (gcov_write_unsigned (da_file, GCOV_TAG_ARC_COUNTS)
318 || !(base = gcov_reserve_length (da_file)))
319 goto write_error;
320
321 for (jx = fn_info->n_arc_counts; jx--;)
322 {
323 gcov_type count = *count_ptr++;
324
325 object.arc_sum += count;
326 if (object.arc_max_sum < count)
327 object.arc_max_sum = count;
328 if (gcov_write_counter (da_file, count))
329 goto write_error; /* RIP Edsger Dijkstra */
330 }
331 if (gcov_write_length (da_file, base))
332 goto write_error;
333 }
334
335 /* Object file summary. */
336 if (gcov_write_summary (da_file, GCOV_TAG_OBJECT_SUMMARY, &object))
337 goto write_error;
338
339 if (merging >= 0)
340 {
341 if (fseek (da_file, 0, SEEK_END))
342 goto write_error;
343 ptr->wkspc = ftell (da_file);
344 if (gcov_write_summary (da_file, GCOV_TAG_PLACEHOLDER_SUMMARY,
345 &program))
346 goto write_error;
347 }
348 else if (ptr->wkspc)
349 {
350 /* Zap trailing program summary */
351 if (fseek (da_file, ptr->wkspc, SEEK_SET))
352 goto write_error;
353 if (!local_prg.runs)
354 ptr->wkspc = 0;
355 if (gcov_write_unsigned (da_file,
356 local_prg.runs ? GCOV_TAG_PLACEHOLDER_SUMMARY
357 : GCOV_TAG_INCORRECT_SUMMARY))
358 goto write_error;
359 }
360 if (fflush (da_file))
361 goto write_error;
362
363 if (fclose (da_file))
364 {
365 fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
366 ptr->filename = 0;
367 }
368 else
369 {
370 program_arcs += ptr->n_arc_counts;
371 program_sum += object.arc_sum;
372 if (program_max_sum < object.arc_max_sum)
373 program_max_sum = object.arc_max_sum;
374 }
375 }
376
377 /* Generate whole program statistics. */
378 program.runs++;
379 program.arcs = program_arcs;
380 program.arc_sum = program_sum;
381 if (program.arc_max_one < program_max_one)
382 program.arc_max_one = program_max_one;
383 if (program.arc_max_sum < program_max_sum)
384 program.arc_max_sum = program_max_sum;
385 program.arc_sum_max += program_max_one;
386
387 /* Upate whole program statistics. */
388 for (ptr = gcov_list; ptr; ptr = ptr->next)
389 if (ptr->filename && ptr->wkspc)
390 {
391 FILE *da_file;
392
393 da_file = fopen (ptr->filename, "r+b");
394 if (!da_file)
395 {
396 fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
397 continue;
398 }
399
400#if defined (TARGET_HAS_F_SETLKW)
401 while (fcntl (fileno (da_file), F_SETLKW, &s_flock)
402 && errno == EINTR)
403 continue;
404#endif
405 if (fseek (da_file, ptr->wkspc, SEEK_SET)
406 || gcov_write_summary (da_file, GCOV_TAG_PROGRAM_SUMMARY, &program)
407 || fflush (da_file))
408 fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
409 if (fclose (da_file))
410 fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
411 }
412}
413
414/* Add a new object file onto the bb chain. Invoked automatically
415 when running an object file's global ctors. */
416
417void
418__gcov_init (struct gcov_info *info)
419{
420 if (!info->version)
421 return;
422 if (info->version != GCOV_VERSION)
423 gcov_version_mismatch (info, info->version);
424 else
425 {
426 const char *ptr = info->filename;
427 unsigned crc32 = gcov_crc32;
428
429 do
430 {
431 unsigned ix;
432 unsigned value = *ptr << 24;
433
434 for (ix = 8; ix--; value <<= 1)
435 {
436 unsigned feedback;
437
438 feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
439 crc32 <<= 1;
440 crc32 ^= feedback;
441 }
442 }
443 while (*ptr++);
444
445 gcov_crc32 = crc32;
446
447 if (!gcov_list)
448 atexit (gcov_exit);
449
450 info->next = gcov_list;
451 gcov_list = info;
452 }
453 info->version = 0;
454}
455
456/* Called before fork or exec - write out profile information gathered so
457 far and reset it to zero. This avoids duplication or loss of the
458 profile information gathered so far. */
459
460void
461__gcov_flush (void)
462{
463 struct gcov_info *ptr;
464
465 gcov_exit ();
466 for (ptr = gcov_list; ptr; ptr = ptr->next)
467 {
468 unsigned i;
469
470 for (i = ptr->n_arc_counts; i--;)
471 ptr->arc_counts[i] = 0;
472 }
473}
This page took 0.101801 seconds and 5 git commands to generate.