OSDN Git Service

* Makefile.in (loop-init.o): Do not depend on gcov-io.h,
[pf3gnuchains/gcc-fork.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 #define IN_LIBGCOV 1
61 #include "gcov-io.h"
62
63 /* Chain of per-object gcov structures.  */
64 static struct gcov_info *gcov_list;
65
66 /* A program checksum allows us to distinguish program data for an
67    object file included in multiple programs.  */
68 static unsigned gcov_crc32;
69
70 static void
71 gcov_version_mismatch (struct gcov_info *ptr, unsigned version)
72 {
73   unsigned expected = GCOV_VERSION;
74   unsigned ix;
75   char e[4], v[4];
76
77   for (ix = 4; ix--; expected >>= 8, version >>= 8)
78     {
79       e[ix] = expected;
80       v[ix] = version;
81     }
82   
83   fprintf (stderr,
84            "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
85            ptr->filename, e, v);
86 }
87
88 /* Dump the coverage counts. We merge with existing counts when
89    possible, to avoid growing the .da files ad infinitum. We use this
90    program's checksum to make sure we only accumulate whole program
91    statistics to the correct summary. An object file might be embedded
92    in two separate programs, and we must keep the two program
93    summaries separate.  */
94
95 static void
96 gcov_exit (void)
97 {
98   struct gcov_info *ptr;
99   unsigned ix, jx;
100   struct gcov_summary program;
101   gcov_type program_max_one = 0;
102   gcov_type program_max_sum = 0;
103   gcov_type program_sum = 0;
104   unsigned program_arcs = 0;
105   
106   memset (&program, 0, sizeof (program));
107   program.checksum = gcov_crc32;
108   
109   for (ptr = gcov_list; ptr; ptr = ptr->next)
110     {
111       struct gcov_summary object;
112       struct gcov_summary local_prg;
113       int error;
114       int merging;
115       unsigned long base;
116       const struct function_info *fn_info;
117       gcov_type **counters;
118       gcov_type *count_ptr;
119       gcov_type object_max_one = 0;
120       unsigned tag, length;
121       unsigned arc_data_index, f_sect_index, sect_index;
122
123       ptr->wkspc = 0;
124       if (!ptr->filename)
125         continue;
126
127       counters = malloc (sizeof (gcov_type *) * ptr->n_counter_sections);
128       for (ix = 0; ix < ptr->n_counter_sections; ix++)
129         counters[ix] = ptr->counter_sections[ix].counters;
130
131       for (arc_data_index = 0;
132            arc_data_index < ptr->n_counter_sections
133            && ptr->counter_sections[arc_data_index].tag != GCOV_TAG_ARC_COUNTS;
134            arc_data_index++)
135         continue;
136
137       if (arc_data_index == ptr->n_counter_sections)
138         {
139           /* For now; later we may want to just measure other profiles,
140              but now I am lazy to check for all consequences.  */
141           abort ();
142         }
143       for (ix = ptr->counter_sections[arc_data_index].n_counters,
144            count_ptr = ptr->counter_sections[arc_data_index].counters; ix--;)
145         {
146           gcov_type count = *count_ptr++;
147
148           if (count > object_max_one)
149             object_max_one = count;
150         }
151       if (object_max_one > program_max_one)
152         program_max_one = object_max_one;
153       
154       memset (&local_prg, 0, sizeof (local_prg));
155       memset (&object, 0, sizeof (object));
156       
157       /* Open for modification */
158       merging = gcov_open (ptr->filename, 0);
159       
160       if (!merging)
161         {
162           fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
163           ptr->filename = 0;
164           continue;
165         }
166       
167       if (merging > 0)
168         {
169           /* Merge data from file.  */
170           if (gcov_read_unsigned () != GCOV_DATA_MAGIC)
171             {
172               fprintf (stderr, "profiling:%s:Not a gcov data file\n",
173                        ptr->filename);
174             read_fatal:;
175               gcov_close ();
176               ptr->filename = 0;
177               continue;
178             }
179           length = gcov_read_unsigned ();
180           if (length != GCOV_VERSION)
181             {
182               gcov_version_mismatch (ptr, length);
183               goto read_fatal;
184             }
185           
186           /* Merge execution counts for each function.  */
187           for (ix = ptr->n_functions, fn_info = ptr->functions;
188                ix--; fn_info++)
189             {
190               tag = gcov_read_unsigned ();
191               length = gcov_read_unsigned ();
192
193               /* Check function */
194               if (tag != GCOV_TAG_FUNCTION)
195                 {
196                 read_mismatch:;
197                   fprintf (stderr, "profiling:%s:Merge mismatch at %s\n",
198                            ptr->filename, fn_info->name);
199                   goto read_fatal;
200                 }
201
202               if (strcmp (gcov_read_string (), fn_info->name)
203                   || gcov_read_unsigned () != fn_info->checksum)
204                 goto read_mismatch;
205
206               /* Counters.  */
207               for (f_sect_index = 0;
208                    f_sect_index < fn_info->n_counter_sections;
209                    f_sect_index++)
210                 {
211                   unsigned n_counters;
212
213                   tag = gcov_read_unsigned ();
214                   length = gcov_read_unsigned ();
215                   
216                   for (sect_index = 0;
217                        sect_index < ptr->n_counter_sections;
218                        sect_index++)
219                     if (ptr->counter_sections[sect_index].tag == tag)
220                       break;
221                   if (sect_index == ptr->n_counter_sections
222                       || fn_info->counter_sections[f_sect_index].tag != tag)
223                     goto read_mismatch;
224
225                   n_counters = fn_info->counter_sections[f_sect_index].n_counters;
226                   if (n_counters != length / 8)
227                     goto read_mismatch;
228                  
229                   for (jx = 0; jx < n_counters; jx++)
230                     counters[sect_index][jx] += gcov_read_counter ();
231                   
232                   counters[sect_index] += n_counters;
233                 }
234               if ((error = gcov_is_error ()))
235                 goto read_error;
236             }
237
238           /* Check object summary */
239           if (gcov_read_unsigned () != GCOV_TAG_OBJECT_SUMMARY)
240             goto read_mismatch;
241           gcov_read_unsigned ();
242           gcov_read_summary (&object);
243
244           /* Check program summary */
245           while (!gcov_is_eof ())
246             {
247               unsigned long base = gcov_position ();
248               
249               tag = gcov_read_unsigned ();
250               gcov_read_unsigned ();
251               if (tag != GCOV_TAG_PROGRAM_SUMMARY
252                   && tag != GCOV_TAG_PLACEHOLDER_SUMMARY
253                   && tag != GCOV_TAG_INCORRECT_SUMMARY)
254                 goto read_mismatch;
255               gcov_read_summary (&local_prg);
256               if ((error = gcov_is_error ()))
257                 {
258                 read_error:;
259                   fprintf (stderr, error < 0 ?
260                            "profiling:%s:Overflow merging\n" :
261                            "profiling:%s:Error merging\n",
262                            ptr->filename);
263                   goto read_fatal;
264                 }
265               
266               if (local_prg.checksum != program.checksum)
267                 continue;
268               if (tag == GCOV_TAG_PLACEHOLDER_SUMMARY)
269                 {
270                   fprintf (stderr,
271                            "profiling:%s:Concurrent race detected\n",
272                            ptr->filename);
273                   goto read_fatal;
274                 }
275               merging = 0;
276               if (tag != GCOV_TAG_PROGRAM_SUMMARY)
277                 break;
278               
279               if (program.runs
280                   && memcmp (&program, &local_prg, sizeof (program)))
281                 {
282                   fprintf (stderr, "profiling:%s:Invocation mismatch\n",
283                            ptr->filename);
284                   local_prg.runs = 0;
285                 }
286               else
287                 memcpy (&program, &local_prg, sizeof (program));
288               ptr->wkspc = base;
289               break;
290             }
291           gcov_seek (0, 0);
292         }
293
294       object.runs++;
295       object.arcs = ptr->counter_sections[arc_data_index].n_counters;
296       object.arc_sum = 0;
297       if (object.arc_max_one < object_max_one)
298         object.arc_max_one = object_max_one;
299       object.arc_sum_max += object_max_one;
300       
301       /* Write out the data.  */
302       gcov_write_unsigned (GCOV_DATA_MAGIC);
303       gcov_write_unsigned (GCOV_VERSION);
304       
305       /* Write execution counts for each function.  */
306       for (ix = 0; ix < ptr->n_counter_sections; ix++)
307         counters[ix] = ptr->counter_sections[ix].counters;
308       for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++)
309         {
310           /* Announce function.  */
311           base = gcov_write_tag (GCOV_TAG_FUNCTION);
312           gcov_write_string (fn_info->name);
313           gcov_write_unsigned (fn_info->checksum);
314           gcov_write_length (base);
315
316           /* counters.  */
317           for (f_sect_index = 0;
318                f_sect_index < fn_info->n_counter_sections;
319                f_sect_index++)
320             {
321               tag = fn_info->counter_sections[f_sect_index].tag;
322               for (sect_index = 0;
323                    sect_index < ptr->n_counter_sections;
324                    sect_index++)
325                 if (ptr->counter_sections[sect_index].tag == tag)
326                   break;
327               if (sect_index == ptr->n_counter_sections)
328                 abort ();
329
330               base = gcov_write_tag (tag);
331               for (jx = fn_info->counter_sections[f_sect_index].n_counters; jx--;)
332                 {
333                   gcov_type count = *counters[sect_index]++;
334               
335                   if (tag == GCOV_TAG_ARC_COUNTS)
336                     {
337                       object.arc_sum += count;
338                       if (object.arc_max_sum < count)
339                         object.arc_max_sum = count;
340                     }
341                   gcov_write_counter (count);
342                 }
343               gcov_write_length (base);
344             }
345         }
346
347       /* Object file summary.  */
348       gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, &object);
349
350       if (merging)
351         {
352           ptr->wkspc = gcov_seek_end ();
353           gcov_write_summary (GCOV_TAG_PLACEHOLDER_SUMMARY, &program);
354         }
355       else if (ptr->wkspc)
356         {
357           /* Zap trailing program summary */
358           gcov_seek (ptr->wkspc, 0);
359           if (!local_prg.runs)
360             ptr->wkspc = 0;
361           gcov_write_unsigned (local_prg.runs
362                                ? GCOV_TAG_PLACEHOLDER_SUMMARY
363                                : GCOV_TAG_INCORRECT_SUMMARY);
364         }
365       if (gcov_close ())
366         {
367           fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
368           ptr->filename = 0;
369         }
370       else
371         {
372           program_arcs += ptr->counter_sections[arc_data_index].n_counters;
373           program_sum += object.arc_sum;
374           if (program_max_sum < object.arc_max_sum)
375             program_max_sum = object.arc_max_sum;
376         }
377       free(counters);
378     }
379
380   /* Generate whole program statistics.  */
381   program.runs++;
382   program.arcs = program_arcs;
383   program.arc_sum = program_sum;
384   if (program.arc_max_one < program_max_one)
385     program.arc_max_one = program_max_one;
386   if (program.arc_max_sum < program_max_sum)
387     program.arc_max_sum = program_max_sum;
388   program.arc_sum_max += program_max_one;
389   
390   /* Upate whole program statistics.  */
391   for (ptr = gcov_list; ptr; ptr = ptr->next)
392     if (ptr->filename && ptr->wkspc)
393       {
394         if (!gcov_open (ptr->filename, 1))
395           {
396             fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
397             continue;
398           }
399         
400         gcov_seek (ptr->wkspc, 0);
401         gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &program);
402         if (gcov_close ())
403           fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
404       }
405 }
406
407 /* Add a new object file onto the bb chain.  Invoked automatically
408    when running an object file's global ctors.  */
409
410 void
411 __gcov_init (struct gcov_info *info)
412 {
413   if (!info->version)
414     return;
415   if (info->version != GCOV_VERSION)
416     gcov_version_mismatch (info, info->version);
417   else
418     {
419       const char *ptr = info->filename;
420       unsigned crc32 = gcov_crc32;
421   
422       do
423         {
424           unsigned ix;
425           unsigned value = *ptr << 24;
426
427           for (ix = 8; ix--; value <<= 1)
428             {
429               unsigned feedback;
430
431               feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
432               crc32 <<= 1;
433               crc32 ^= feedback;
434             }
435         }
436       while (*ptr++);
437       
438       gcov_crc32 = crc32;
439       
440       if (!gcov_list)
441         atexit (gcov_exit);
442       
443       info->next = gcov_list;
444       gcov_list = info;
445     }
446   info->version = 0;
447 }
448
449 /* Called before fork or exec - write out profile information gathered so
450    far and reset it to zero.  This avoids duplication or loss of the
451    profile information gathered so far.  */
452
453 void
454 __gcov_flush (void)
455 {
456   struct gcov_info *ptr;
457
458   gcov_exit ();
459   for (ptr = gcov_list; ptr; ptr = ptr->next)
460     {
461       unsigned i, j;
462       
463       for (j = 0; j < ptr->n_counter_sections; j++)
464         for (i = ptr->counter_sections[j].n_counters; i--;)
465           ptr->counter_sections[j].counters[i] = 0;
466     }
467 }
468
469 #endif /* inhibit_libc */