OSDN Git Service

* gcov-io.h (gcov_save_position, gcov_reserve_length, gcov_resync,
[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 <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 }