OSDN Git Service

* Makefile.in (LIB2FUNCS_ST): Remove _gcov.
[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 /* 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.  */
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       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
417 void
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
460 void
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 }