OSDN Git Service

2003-03-27 Janis Johnson <janis187@us.ibm.com>
[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 #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 */