libgcov.c: Provide only dummy functions if libc is not available.
[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 #if defined(inhibit_libc)
33 /* If libc and its header files are not available, provide dummy functions. */
34
35 void __gcov_init (void *p);
36 void __gcov_flush (void);
37
38 void __gcov_init (void *p) { }
39 void __gcov_flush (void) { }
40
41 #else
42
43 /* It is incorrect to include config.h here, because this file is being
44 compiled for the target, and hence definitions concerning only the host
45 do not apply. */
46
47 #include "tconfig.h"
48 #include "tsystem.h"
49 #include "coretypes.h"
50 #include "tm.h"
51
52 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
53 #include <stdio.h>
54
55 #include <string.h>
56 #if defined (TARGET_HAS_F_SETLKW)
57 #include <fcntl.h>
58 #include <errno.h>
59 #endif
60 #include "gcov-io.h"
61
62 /* Chain of per-object gcov structures. */
63 static struct gcov_info *gcov_list;
64
65 /* A program checksum allows us to distinguish program data for an
66 object file included in multiple programs. */
67 static unsigned gcov_crc32;
68
69 static void
70 gcov_version_mismatch (struct gcov_info *ptr, unsigned version)
71 {
72 unsigned expected = GCOV_VERSION;
73 unsigned ix;
74 char e[4], v[4];
75
76 for (ix = 4; ix--; expected >>= 8, version >>= 8)
77 {
78 e[ix] = expected;
79 v[ix] = version;
80 }
81
82 fprintf (stderr,
83 "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
84 ptr->filename, e, v);
85 }
86
87 /* Dump the coverage counts. We merge with existing counts when
88 possible, to avoid growing the .da files ad infinitum. We use this
89 program's checksum to make sure we only accumulate whole program
90 statistics to the correct summary. An object file might be embedded
91 in two separate programs, and we must keep the two program
92 summaries separate. */
93
94 static void
95 gcov_exit (void)
96 {
97 struct gcov_info *ptr;
98 unsigned ix, jx;
99 struct gcov_summary program;
100 gcov_type program_max_one = 0;
101 gcov_type program_max_sum = 0;
102 gcov_type program_sum = 0;
103 unsigned program_arcs = 0;
104
105 #if defined (TARGET_HAS_F_SETLKW)
106 struct flock s_flock;
107
108 s_flock.l_type = F_WRLCK;
109 s_flock.l_whence = SEEK_SET;
110 s_flock.l_start = 0;
111 s_flock.l_len = 0; /* Until EOF. */
112 s_flock.l_pid = getpid ();
113 #endif
114
115 memset (&program, 0, sizeof (program));
116 program.checksum = gcov_crc32;
117
118 for (ptr = gcov_list; ptr; ptr = ptr->next)
119 {
120 struct gcov_summary object;
121 struct gcov_summary local_prg;
122 int merging = 0;
123 long base;
124 const struct function_info *fn_info;
125 gcov_type **counters;
126 gcov_type *count_ptr;
127 gcov_type object_max_one = 0;
128 gcov_type count;
129 unsigned tag, length, flength, checksum;
130 unsigned arc_data_index, f_sect_index, sect_index;
131
132 ptr->wkspc = 0;
133 if (!ptr->filename)
134 continue;
135
136 counters = malloc (sizeof (gcov_type *) * ptr->n_counter_sections);
137 for (ix = 0; ix < ptr->n_counter_sections; ix++)
138 counters[ix] = ptr->counter_sections[ix].counters;
139
140 for (arc_data_index = 0;
141 arc_data_index < ptr->n_counter_sections
142 && ptr->counter_sections[arc_data_index].tag != GCOV_TAG_ARC_COUNTS;
143 arc_data_index++)
144 continue;
145
146 if (arc_data_index == ptr->n_counter_sections)
147 {
148 /* For now; later we may want to just measure other profiles,
149 but now I am lazy to check for all consequences. */
150 abort ();
151 }
152 for (ix = ptr->counter_sections[arc_data_index].n_counters,
153 count_ptr = ptr->counter_sections[arc_data_index].counters; ix--;)
154 {
155 gcov_type count = *count_ptr++;
156
157 if (count > object_max_one)
158 object_max_one = count;
159 }
160 if (object_max_one > program_max_one)
161 program_max_one = object_max_one;
162
163 memset (&local_prg, 0, sizeof (local_prg));
164 memset (&object, 0, sizeof (object));
165
166 /* Open for modification */
167 if (!da_file_open (ptr->filename, &merging))
168 {
169 fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
170 ptr->filename = 0;
171 continue;
172 }
173
174 if (merging)
175 {
176 /* Merge data from file. */
177
178 if (gcov_read_unsigned (0, &tag) || tag != GCOV_DATA_MAGIC)
179 {
180 fprintf (stderr, "profiling:%s:Not a gcov data file\n",
181 ptr->filename);
182 read_fatal:;
183 da_file_close ();
184 ptr->filename = 0;
185 continue;
186 }
187 if (gcov_read_unsigned (0, &length) || length != GCOV_VERSION)
188 {
189 gcov_version_mismatch (ptr, length);
190 goto read_fatal;
191 }
192
193 /* Merge execution counts for each function. */
194 for (ix = ptr->n_functions, fn_info = ptr->functions;
195 ix--; fn_info++)
196 {
197 if (gcov_read_unsigned (0, &tag)
198 || gcov_read_unsigned (0, &length))
199 {
200 read_error:;
201 fprintf (stderr, "profiling:%s:Error merging\n",
202 ptr->filename);
203 goto read_fatal;
204 }
205
206 /* Check function */
207 if (tag != GCOV_TAG_FUNCTION)
208 {
209 read_mismatch:;
210 fprintf (stderr, "profiling:%s:Merge mismatch at %s\n",
211 ptr->filename, fn_info->name);
212 goto read_fatal;
213 }
214
215 if (gcov_read_unsigned (0, &flength)
216 || gcov_skip_string (0, flength)
217 || gcov_read_unsigned (0, &checksum))
218 goto read_error;
219 if (flength != strlen (fn_info->name)
220 || checksum != fn_info->checksum)
221 goto read_mismatch;
222
223 /* Counters. */
224 for (f_sect_index = 0;
225 f_sect_index < fn_info->n_counter_sections;
226 f_sect_index++)
227 {
228 unsigned n_counters;
229
230 if (gcov_read_unsigned (0, &tag)
231 || gcov_read_unsigned (0, &length))
232 goto read_error;
233 for (sect_index = 0;
234 sect_index < ptr->n_counter_sections;
235 sect_index++)
236 if (ptr->counter_sections[sect_index].tag == tag)
237 break;
238 if (sect_index == ptr->n_counter_sections
239 || fn_info->counter_sections[f_sect_index].tag != tag)
240 goto read_mismatch;
241
242 n_counters = fn_info->counter_sections[f_sect_index].n_counters;
243 if (n_counters != length / 8)
244 goto read_mismatch;
245
246 for (jx = 0; jx < n_counters; jx++)
247 if (gcov_read_counter (0, &count))
248 goto read_error;
249 else
250 counters[sect_index][jx] += count;
251 counters[sect_index] += n_counters;
252 }
253 }
254
255 /* Check object summary */
256 if (gcov_read_unsigned (0, &tag)
257 || gcov_read_unsigned (0, &length))
258 goto read_error;
259 if (tag != GCOV_TAG_OBJECT_SUMMARY)
260 goto read_mismatch;
261 if (gcov_read_summary (0, &object))
262 goto read_error;
263
264 /* Check program summary */
265 while (1)
266 {
267 long base = da_file_position (0);
268
269 if (gcov_read_unsigned (0, &tag)
270 || gcov_read_unsigned (0, &length))
271 {
272 if (da_file_eof ())
273 break;
274 goto read_error;
275 }
276 if (tag != GCOV_TAG_PROGRAM_SUMMARY
277 && tag != GCOV_TAG_PLACEHOLDER_SUMMARY
278 && tag != GCOV_TAG_INCORRECT_SUMMARY)
279 goto read_mismatch;
280 if (gcov_read_summary (0, &local_prg))
281 goto read_error;
282 if (local_prg.checksum != program.checksum)
283 continue;
284 if (tag == GCOV_TAG_PLACEHOLDER_SUMMARY)
285 {
286 fprintf (stderr,
287 "profiling:%s:Concurrent race detected\n",
288 ptr->filename);
289 goto read_fatal;
290 }
291 merging = -1;
292 if (tag != GCOV_TAG_PROGRAM_SUMMARY)
293 break;
294
295 if (program.runs
296 && memcmp (&program, &local_prg, sizeof (program)))
297 {
298 fprintf (stderr, "profiling:%s:Invocation mismatch\n",
299 ptr->filename);
300 local_prg.runs = 0;
301 }
302 else
303 memcpy (&program, &local_prg, sizeof (program));
304 ptr->wkspc = base;
305 break;
306 }
307 da_file_seek (0, 0, SEEK_SET);
308 }
309
310 object.runs++;
311 object.arcs = ptr->counter_sections[arc_data_index].n_counters;
312 object.arc_sum = 0;
313 if (object.arc_max_one < object_max_one)
314 object.arc_max_one = object_max_one;
315 object.arc_sum_max += object_max_one;
316
317 /* Write out the data. */
318 if (/* magic */
319 gcov_write_unsigned (0, GCOV_DATA_MAGIC)
320 /* version number */
321 || gcov_write_unsigned (0, GCOV_VERSION))
322 {
323 write_error:;
324 da_file_close ();
325 fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
326 ptr->filename = 0;
327 continue;
328 }
329
330 /* Write execution counts for each function. */
331 for (ix = 0; ix < ptr->n_counter_sections; ix++)
332 counters[ix] = ptr->counter_sections[ix].counters;
333 for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++)
334 {
335 /* Announce function. */
336 if (gcov_write_unsigned (0, GCOV_TAG_FUNCTION)
337 || !(base = gcov_reserve_length (0))
338 /* function name */
339 || gcov_write_string (0, fn_info->name,
340 strlen (fn_info->name))
341 /* function checksum */
342 || gcov_write_unsigned (0, fn_info->checksum)
343 || gcov_write_length (0, base))
344 goto write_error;
345
346 /* counters. */
347 for (f_sect_index = 0;
348 f_sect_index < fn_info->n_counter_sections;
349 f_sect_index++)
350 {
351 tag = fn_info->counter_sections[f_sect_index].tag;
352 for (sect_index = 0;
353 sect_index < ptr->n_counter_sections;
354 sect_index++)
355 if (ptr->counter_sections[sect_index].tag == tag)
356 break;
357 if (sect_index == ptr->n_counter_sections)
358 abort ();
359
360 if (gcov_write_unsigned (0, tag)
361 || !(base = gcov_reserve_length (0)))
362 goto write_error;
363
364 for (jx = fn_info->counter_sections[f_sect_index].n_counters; jx--;)
365 {
366 gcov_type count = *counters[sect_index]++;
367
368 if (tag == GCOV_TAG_ARC_COUNTS)
369 {
370 object.arc_sum += count;
371 if (object.arc_max_sum < count)
372 object.arc_max_sum = count;
373 }
374 if (gcov_write_counter (0, count))
375 goto write_error; /* RIP Edsger Dijkstra */
376 }
377 if (gcov_write_length (0, base))
378 goto write_error;
379 }
380 }
381
382 /* Object file summary. */
383 if (gcov_write_summary (0, GCOV_TAG_OBJECT_SUMMARY, &object))
384 goto write_error;
385
386 if (merging >= 0)
387 {
388 if (da_file_seek (0, 0, SEEK_END))
389 goto write_error;
390 ptr->wkspc = da_file_position (0);
391 if (gcov_write_summary (0, GCOV_TAG_PLACEHOLDER_SUMMARY,
392 &program))
393 goto write_error;
394 }
395 else if (ptr->wkspc)
396 {
397 /* Zap trailing program summary */
398 if (da_file_seek (0, ptr->wkspc, SEEK_SET))
399 goto write_error;
400 if (!local_prg.runs)
401 ptr->wkspc = 0;
402 if (gcov_write_unsigned (0, local_prg.runs
403 ? GCOV_TAG_PLACEHOLDER_SUMMARY
404 : GCOV_TAG_INCORRECT_SUMMARY))
405 goto write_error;
406 }
407
408 if (da_file_close ())
409 {
410 fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
411 ptr->filename = 0;
412 }
413 else
414 {
415 program_arcs += ptr->counter_sections[arc_data_index].n_counters;
416 program_sum += object.arc_sum;
417 if (program_max_sum < object.arc_max_sum)
418 program_max_sum = object.arc_max_sum;
419 }
420 free(counters);
421 }
422
423 /* Generate whole program statistics. */
424 program.runs++;
425 program.arcs = program_arcs;
426 program.arc_sum = program_sum;
427 if (program.arc_max_one < program_max_one)
428 program.arc_max_one = program_max_one;
429 if (program.arc_max_sum < program_max_sum)
430 program.arc_max_sum = program_max_sum;
431 program.arc_sum_max += program_max_one;
432
433 /* Upate whole program statistics. */
434 for (ptr = gcov_list; ptr; ptr = ptr->next)
435 if (ptr->filename && ptr->wkspc)
436 {
437 FILE *da_file;
438
439 da_file = fopen (ptr->filename, "r+b");
440 if (!da_file)
441 {
442 fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
443 continue;
444 }
445
446 #if defined (TARGET_HAS_F_SETLKW)
447 while (fcntl (fileno (da_file), F_SETLKW, &s_flock)
448 && errno == EINTR)
449 continue;
450 #endif
451 if (fseek (da_file, ptr->wkspc, SEEK_SET)
452 || gcov_write_summary (da_file, GCOV_TAG_PROGRAM_SUMMARY, &program)
453 || fflush (da_file))
454 fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
455 if (fclose (da_file))
456 fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
457 }
458 }
459
460 /* Add a new object file onto the bb chain. Invoked automatically
461 when running an object file's global ctors. */
462
463 void
464 __gcov_init (struct gcov_info *info)
465 {
466 if (!info->version)
467 return;
468 if (info->version != GCOV_VERSION)
469 gcov_version_mismatch (info, info->version);
470 else
471 {
472 const char *ptr = info->filename;
473 unsigned crc32 = gcov_crc32;
474
475 do
476 {
477 unsigned ix;
478 unsigned value = *ptr << 24;
479
480 for (ix = 8; ix--; value <<= 1)
481 {
482 unsigned feedback;
483
484 feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
485 crc32 <<= 1;
486 crc32 ^= feedback;
487 }
488 }
489 while (*ptr++);
490
491 gcov_crc32 = crc32;
492
493 if (!gcov_list)
494 atexit (gcov_exit);
495
496 info->next = gcov_list;
497 gcov_list = info;
498 }
499 info->version = 0;
500 }
501
502 /* Called before fork or exec - write out profile information gathered so
503 far and reset it to zero. This avoids duplication or loss of the
504 profile information gathered so far. */
505
506 void
507 __gcov_flush (void)
508 {
509 struct gcov_info *ptr;
510
511 gcov_exit ();
512 for (ptr = gcov_list; ptr; ptr = ptr->next)
513 {
514 unsigned i, j;
515
516 for (j = 0; j < ptr->n_counter_sections; j++)
517 for (i = ptr->counter_sections[j].n_counters; i--;)
518 ptr->counter_sections[j].counters[i] = 0;
519 }
520 }
521
522 #endif /* inhibit_libc */