8540.md: New file.
[gcc.git] / gcc / libgcov.c
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
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 In addition to the permissions in the GNU General Public License, the
14 Free Software Foundation gives you unlimited permission to link the
15 compiled version of this file into combinations with other programs,
16 and to distribute those combinations without any restriction coming
17 from the use of this file. (The General Public License restrictions
18 do apply in other respects; for example, they cover modification of
19 the file, and distribution when not linked into a combine
20 executable.)
21
22 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
23 WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with GCC; see the file COPYING. If not, write to the Free
29 Software Foundation, 59 Temple Place - Suite 330, Boston, MA
30 02111-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 <string.h>
45 #if defined (TARGET_HAS_F_SETLKW)
46 #include <fcntl.h>
47 #include <errno.h>
48 #endif
49 #include "gcov-io.h"
50
51 /* Chain of per-object gcov structures. */
52 static 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. */
56 static unsigned gcov_crc32;
57
58 static void
59 gcov_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
83 static void
84 gcov_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 struct gcov_summary object;
110 struct gcov_summary local_prg;
111 int merging = 0;
112 long base;
113 const struct function_info *fn_info;
114 gcov_type **counters;
115 gcov_type *count_ptr;
116 gcov_type object_max_one = 0;
117 gcov_type count;
118 unsigned tag, length, flength, checksum;
119 unsigned arc_data_index, f_sect_index, sect_index;
120
121 ptr->wkspc = 0;
122 if (!ptr->filename)
123 continue;
124
125 counters = malloc (sizeof (gcov_type *) * ptr->n_counter_sections);
126 for (ix = 0; ix < ptr->n_counter_sections; ix++)
127 counters[ix] = ptr->counter_sections[ix].counters;
128
129 for (arc_data_index = 0;
130 arc_data_index < ptr->n_counter_sections
131 && ptr->counter_sections[arc_data_index].tag != GCOV_TAG_ARC_COUNTS;
132 arc_data_index++)
133 continue;
134
135 if (arc_data_index == ptr->n_counter_sections)
136 {
137 /* For now; later we may want to just measure other profiles,
138 but now I am lazy to check for all consequences. */
139 abort ();
140 }
141 for (ix = ptr->counter_sections[arc_data_index].n_counters,
142 count_ptr = ptr->counter_sections[arc_data_index].counters; ix--;)
143 {
144 gcov_type count = *count_ptr++;
145
146 if (count > object_max_one)
147 object_max_one = count;
148 }
149 if (object_max_one > program_max_one)
150 program_max_one = object_max_one;
151
152 memset (&local_prg, 0, sizeof (local_prg));
153 memset (&object, 0, sizeof (object));
154
155 /* Open for modification */
156 if (!da_file_open (ptr->filename, &merging))
157 {
158 fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
159 ptr->filename = 0;
160 continue;
161 }
162
163 if (merging)
164 {
165 /* Merge data from file. */
166
167 if (gcov_read_unsigned (0, &tag) || tag != GCOV_DATA_MAGIC)
168 {
169 fprintf (stderr, "profiling:%s:Not a gcov data file\n",
170 ptr->filename);
171 read_fatal:;
172 da_file_close ();
173 ptr->filename = 0;
174 continue;
175 }
176 if (gcov_read_unsigned (0, &length) || length != GCOV_VERSION)
177 {
178 gcov_version_mismatch (ptr, length);
179 goto read_fatal;
180 }
181
182 /* Merge execution counts for each function. */
183 for (ix = ptr->n_functions, fn_info = ptr->functions;
184 ix--; fn_info++)
185 {
186 if (gcov_read_unsigned (0, &tag)
187 || gcov_read_unsigned (0, &length))
188 {
189 read_error:;
190 fprintf (stderr, "profiling:%s:Error merging\n",
191 ptr->filename);
192 goto read_fatal;
193 }
194
195 /* Check function */
196 if (tag != GCOV_TAG_FUNCTION)
197 {
198 read_mismatch:;
199 fprintf (stderr, "profiling:%s:Merge mismatch at %s\n",
200 ptr->filename, fn_info->name);
201 goto read_fatal;
202 }
203
204 if (gcov_read_unsigned (0, &flength)
205 || gcov_skip_string (0, flength)
206 || gcov_read_unsigned (0, &checksum))
207 goto read_error;
208 if (flength != strlen (fn_info->name)
209 || checksum != fn_info->checksum)
210 goto read_mismatch;
211
212 /* Counters. */
213 for (f_sect_index = 0;
214 f_sect_index < fn_info->n_counter_sections;
215 f_sect_index++)
216 {
217 unsigned n_counters;
218
219 if (gcov_read_unsigned (0, &tag)
220 || gcov_read_unsigned (0, &length))
221 goto read_error;
222 for (sect_index = 0;
223 sect_index < ptr->n_counter_sections;
224 sect_index++)
225 if (ptr->counter_sections[sect_index].tag == tag)
226 break;
227 if (sect_index == ptr->n_counter_sections
228 || fn_info->counter_sections[f_sect_index].tag != tag)
229 goto read_mismatch;
230
231 n_counters = fn_info->counter_sections[f_sect_index].n_counters;
232 if (n_counters != length / 8)
233 goto read_mismatch;
234
235 for (jx = 0; jx < n_counters; jx++)
236 if (gcov_read_counter (0, &count))
237 goto read_error;
238 else
239 counters[sect_index][jx] += count;
240 counters[sect_index] += n_counters;
241 }
242 }
243
244 /* Check object summary */
245 if (gcov_read_unsigned (0, &tag)
246 || gcov_read_unsigned (0, &length))
247 goto read_error;
248 if (tag != GCOV_TAG_OBJECT_SUMMARY)
249 goto read_mismatch;
250 if (gcov_read_summary (0, &object))
251 goto read_error;
252
253 /* Check program summary */
254 while (1)
255 {
256 long base = da_file_position (0);
257
258 if (gcov_read_unsigned (0, &tag)
259 || gcov_read_unsigned (0, &length))
260 {
261 if (da_file_eof ())
262 break;
263 goto read_error;
264 }
265 if (tag != GCOV_TAG_PROGRAM_SUMMARY
266 && tag != GCOV_TAG_PLACEHOLDER_SUMMARY
267 && tag != GCOV_TAG_INCORRECT_SUMMARY)
268 goto read_mismatch;
269 if (gcov_read_summary (0, &local_prg))
270 goto read_error;
271 if (local_prg.checksum != program.checksum)
272 continue;
273 if (tag == GCOV_TAG_PLACEHOLDER_SUMMARY)
274 {
275 fprintf (stderr,
276 "profiling:%s:Concurrent race detected\n",
277 ptr->filename);
278 goto read_fatal;
279 }
280 merging = -1;
281 if (tag != GCOV_TAG_PROGRAM_SUMMARY)
282 break;
283
284 if (program.runs
285 && memcmp (&program, &local_prg, sizeof (program)))
286 {
287 fprintf (stderr, "profiling:%s:Invocation mismatch\n",
288 ptr->filename);
289 local_prg.runs = 0;
290 }
291 else
292 memcpy (&program, &local_prg, sizeof (program));
293 ptr->wkspc = base;
294 break;
295 }
296 da_file_seek (0, 0, SEEK_SET);
297 }
298
299 object.runs++;
300 object.arcs = ptr->counter_sections[arc_data_index].n_counters;
301 object.arc_sum = 0;
302 if (object.arc_max_one < object_max_one)
303 object.arc_max_one = object_max_one;
304 object.arc_sum_max += object_max_one;
305
306 /* Write out the data. */
307 if (/* magic */
308 gcov_write_unsigned (0, GCOV_DATA_MAGIC)
309 /* version number */
310 || gcov_write_unsigned (0, GCOV_VERSION))
311 {
312 write_error:;
313 da_file_close ();
314 fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
315 ptr->filename = 0;
316 continue;
317 }
318
319 /* Write execution counts for each function. */
320 for (ix = 0; ix < ptr->n_counter_sections; ix++)
321 counters[ix] = ptr->counter_sections[ix].counters;
322 for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++)
323 {
324 /* Announce function. */
325 if (gcov_write_unsigned (0, GCOV_TAG_FUNCTION)
326 || !(base = gcov_reserve_length (0))
327 /* function name */
328 || gcov_write_string (0, fn_info->name,
329 strlen (fn_info->name))
330 /* function checksum */
331 || gcov_write_unsigned (0, fn_info->checksum)
332 || gcov_write_length (0, base))
333 goto write_error;
334
335 /* counters. */
336 for (f_sect_index = 0;
337 f_sect_index < fn_info->n_counter_sections;
338 f_sect_index++)
339 {
340 tag = fn_info->counter_sections[f_sect_index].tag;
341 for (sect_index = 0;
342 sect_index < ptr->n_counter_sections;
343 sect_index++)
344 if (ptr->counter_sections[sect_index].tag == tag)
345 break;
346 if (sect_index == ptr->n_counter_sections)
347 abort ();
348
349 if (gcov_write_unsigned (0, tag)
350 || !(base = gcov_reserve_length (0)))
351 goto write_error;
352
353 for (jx = fn_info->counter_sections[f_sect_index].n_counters; jx--;)
354 {
355 gcov_type count = *counters[sect_index]++;
356
357 if (tag == GCOV_TAG_ARC_COUNTS)
358 {
359 object.arc_sum += count;
360 if (object.arc_max_sum < count)
361 object.arc_max_sum = count;
362 }
363 if (gcov_write_counter (0, count))
364 goto write_error; /* RIP Edsger Dijkstra */
365 }
366 if (gcov_write_length (0, base))
367 goto write_error;
368 }
369 }
370
371 /* Object file summary. */
372 if (gcov_write_summary (0, GCOV_TAG_OBJECT_SUMMARY, &object))
373 goto write_error;
374
375 if (merging >= 0)
376 {
377 if (da_file_seek (0, 0, SEEK_END))
378 goto write_error;
379 ptr->wkspc = da_file_position (0);
380 if (gcov_write_summary (0, GCOV_TAG_PLACEHOLDER_SUMMARY,
381 &program))
382 goto write_error;
383 }
384 else if (ptr->wkspc)
385 {
386 /* Zap trailing program summary */
387 if (da_file_seek (0, ptr->wkspc, SEEK_SET))
388 goto write_error;
389 if (!local_prg.runs)
390 ptr->wkspc = 0;
391 if (gcov_write_unsigned (0, local_prg.runs
392 ? GCOV_TAG_PLACEHOLDER_SUMMARY
393 : GCOV_TAG_INCORRECT_SUMMARY))
394 goto write_error;
395 }
396
397 if (da_file_close ())
398 {
399 fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
400 ptr->filename = 0;
401 }
402 else
403 {
404 program_arcs += ptr->counter_sections[arc_data_index].n_counters;
405 program_sum += object.arc_sum;
406 if (program_max_sum < object.arc_max_sum)
407 program_max_sum = object.arc_max_sum;
408 }
409 free(counters);
410 }
411
412 /* Generate whole program statistics. */
413 program.runs++;
414 program.arcs = program_arcs;
415 program.arc_sum = program_sum;
416 if (program.arc_max_one < program_max_one)
417 program.arc_max_one = program_max_one;
418 if (program.arc_max_sum < program_max_sum)
419 program.arc_max_sum = program_max_sum;
420 program.arc_sum_max += program_max_one;
421
422 /* Upate whole program statistics. */
423 for (ptr = gcov_list; ptr; ptr = ptr->next)
424 if (ptr->filename && ptr->wkspc)
425 {
426 FILE *da_file;
427
428 da_file = fopen (ptr->filename, "r+b");
429 if (!da_file)
430 {
431 fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
432 continue;
433 }
434
435 #if defined (TARGET_HAS_F_SETLKW)
436 while (fcntl (fileno (da_file), F_SETLKW, &s_flock)
437 && errno == EINTR)
438 continue;
439 #endif
440 if (fseek (da_file, ptr->wkspc, SEEK_SET)
441 || gcov_write_summary (da_file, GCOV_TAG_PROGRAM_SUMMARY, &program)
442 || fflush (da_file))
443 fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
444 if (fclose (da_file))
445 fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
446 }
447 }
448
449 /* Add a new object file onto the bb chain. Invoked automatically
450 when running an object file's global ctors. */
451
452 void
453 __gcov_init (struct gcov_info *info)
454 {
455 if (!info->version)
456 return;
457 if (info->version != GCOV_VERSION)
458 gcov_version_mismatch (info, info->version);
459 else
460 {
461 const char *ptr = info->filename;
462 unsigned crc32 = gcov_crc32;
463
464 do
465 {
466 unsigned ix;
467 unsigned value = *ptr << 24;
468
469 for (ix = 8; ix--; value <<= 1)
470 {
471 unsigned feedback;
472
473 feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
474 crc32 <<= 1;
475 crc32 ^= feedback;
476 }
477 }
478 while (*ptr++);
479
480 gcov_crc32 = crc32;
481
482 if (!gcov_list)
483 atexit (gcov_exit);
484
485 info->next = gcov_list;
486 gcov_list = info;
487 }
488 info->version = 0;
489 }
490
491 /* Called before fork or exec - write out profile information gathered so
492 far and reset it to zero. This avoids duplication or loss of the
493 profile information gathered so far. */
494
495 void
496 __gcov_flush (void)
497 {
498 struct gcov_info *ptr;
499
500 gcov_exit ();
501 for (ptr = gcov_list; ptr; ptr = ptr->next)
502 {
503 unsigned i, j;
504
505 for (j = 0; j < ptr->n_counter_sections; j++)
506 for (i = ptr->counter_sections[j].n_counters; i--;)
507 ptr->counter_sections[j].counters[i] = 0;
508 }
509 }