b084d0d06eeeab0719df657b2f603c6231332dd7
[platform/upstream/dbus.git] / test / decode-gcov.c
1  /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* decode-gcov.c gcov decoder program
3  *
4  * Copyright (C) 2003  Red Hat Inc.
5  *
6  * Partially derived from gcov,
7  * Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
8  * 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
9  *
10  * This file is NOT licensed under the Academic Free License
11  * as it is largely derived from gcov.c and gcov-io.h in the
12  * gcc source code.
13  * 
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  *
28  */
29
30 #define DBUS_COMPILATION /* cheat */
31 #include <dbus/dbus-list.h>
32 #include <dbus/dbus-string.h>
33 #include <dbus/dbus-sysdeps.h>
34 #include <dbus/dbus-marshal.h>
35 #include <dbus/dbus-hash.h>
36 #undef DBUS_COMPILATION
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #ifndef DBUS_HAVE_INT64
42 #error "gcov support can't be built without 64-bit integer support"
43 #endif
44
45 static void
46 die (const char *message)
47 {
48   fprintf (stderr, "%s", message);
49   exit (1);
50 }
51
52 /* This bizarro function is from gcov-io.h in gcc source tree */
53 static int
54 fetch_long (long        *dest,
55             const char  *source,
56             size_t       bytes)
57 {
58   long value = 0;
59   int i;
60                                                                                 
61   for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
62     if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
63       return 1;
64                                                                                 
65   for (; i >= 0; i--)
66     value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
67                                                                                 
68   if ((source[bytes - 1] & 128) && (value > 0))
69     value = - value;
70                                                                                 
71   *dest = value;
72   return 0;
73 }
74
75 static int
76 fetch_long64 (dbus_int64_t *dest,
77               const char   *source,
78               size_t        bytes)
79 {
80   dbus_int64_t value = 0;
81   int i;
82                                                                                 
83   for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
84     if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
85       return 1;
86                                                                                 
87   for (; i >= 0; i--)
88     value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
89                                                                                 
90   if ((source[bytes - 1] & 128) && (value > 0))
91     value = - value;
92                                                                                 
93   *dest = value;
94   return 0;
95 }
96
97 #define BB_FILENAME     (-1)
98 #define BB_FUNCTION     (-2)
99 #define BB_ENDOFLIST    0
100
101 static dbus_bool_t
102 string_get_int (const DBusString *str,
103                 int               start,
104                 long             *val)
105 {
106   const char *p;
107   
108   if ((_dbus_string_get_length (str) - start) < 4)
109     return FALSE;
110
111   p = _dbus_string_get_const_data (str);
112
113   p += start;
114
115   fetch_long (val, p, 4);
116   
117   return TRUE;
118 }
119
120 static dbus_bool_t
121 string_get_int64 (const DBusString *str,
122                   int               start,
123                   dbus_int64_t     *val)
124 {
125   const char *p;
126   
127   if ((_dbus_string_get_length (str) - start) < 8)
128     return FALSE;
129
130   p = _dbus_string_get_const_data (str);
131
132   p += start;
133
134   fetch_long64 (val, p, 8);
135   
136   return TRUE;
137 }
138
139 static dbus_bool_t
140 string_get_string (const DBusString *str,
141                    int               start,
142                    long              terminator,
143                    DBusString       *val,
144                    int              *end)
145 {
146   int i;
147   long n;
148   
149   i = start;
150   while (string_get_int (str, i, &n))
151     {
152       unsigned char b;
153       
154       i += 4;
155       
156       if (n == terminator)
157         break;
158
159       b = n & 0xff;
160       if (b)
161         {
162           _dbus_string_append_byte (val, b);
163           b = (n >> 8) & 0xff;
164           if (b)
165             {
166               _dbus_string_append_byte (val, b);
167               b = (n >> 16) & 0xff;
168               if (b)
169                 {
170                   _dbus_string_append_byte (val, b);
171                   b = (n >> 24) & 0xff;
172                   if (b)
173                     _dbus_string_append_byte (val, b);
174                 }
175             }
176         }
177     }
178
179   *end = i;
180   
181   return TRUE;
182 }
183
184 #ifdef DBUS_HAVE_GCC33_GCOV
185 /* In gcc33 .bbg files, there's a function name of the form:
186  *   -1, length, name (padded to 4), -1, checksum
187  */
188 static dbus_bool_t
189 string_get_function (const DBusString *str,
190                      int               start,
191                      DBusString       *funcname,
192                      int              *checksum,
193                      int              *next)
194 {
195   int end;
196   long val;
197   int i;
198
199   i = start;
200   
201   if (!string_get_int (str, i, &val))
202     die ("no room for -1 before function name\n");
203         
204   i += 4;
205
206   if (val != -1)
207     die ("value before function name is not -1\n");
208   
209   if (!string_get_int (str, i, &val))
210     die ("no length found for function name\n");
211         
212   i += 4;
213
214   end = i + val;
215   if (end > _dbus_string_get_length (str))
216     die ("Function name length points past end of file\n");
217
218   if (!_dbus_string_append (funcname,
219                             _dbus_string_get_const_data (str) + i))
220     die ("no memory\n");
221         
222   /* skip alignment padding the length doesn't include the nul so add 1
223    */
224   i = _DBUS_ALIGN_VALUE (end + 1, 4);
225         
226   if (!string_get_int (str, i, &val) ||
227       val != -1)
228     die ("-1 at end of function name not found\n");
229         
230   i += 4;
231
232   if (!string_get_int (str, i, &val))
233     die ("no checksum found at end of function name\n");
234         
235   i += 4;
236
237   *checksum = val;
238
239   *next = i;
240
241   return TRUE;
242 }
243 #endif /* DBUS_HAVE_GCC33_GCOV */
244
245 static void
246 dump_bb_file (const DBusString *contents)
247 {
248   int i;
249   long val;
250   int n_functions;
251
252   n_functions = 0;
253   i = 0;
254   while (string_get_int (contents, i, &val))
255     {
256       i += 4;
257       
258       switch (val)
259         {
260         case BB_FILENAME:
261           {
262             DBusString f;
263
264             if (!_dbus_string_init (&f))
265               die ("no memory\n");
266
267             if (string_get_string (contents, i,
268                                    BB_FILENAME,
269                                    &f, &i))
270               {
271                 printf ("File %s\n", _dbus_string_get_const_data (&f));
272               }
273             _dbus_string_free (&f);
274           }
275           break;
276         case BB_FUNCTION:
277           {
278             DBusString f;
279             if (!_dbus_string_init (&f))
280               die ("no memory\n");
281
282             if (string_get_string (contents, i,
283                                    BB_FUNCTION,
284                                    &f, &i))
285               {
286                 printf ("Function %s\n", _dbus_string_get_const_data (&f));
287               }
288             _dbus_string_free (&f);
289
290             n_functions += 1;
291           }
292           break;
293         case BB_ENDOFLIST:
294           printf ("End of block\n");
295           break;
296         default:
297           printf ("Line %ld\n", val);
298           break;
299         }
300     }
301
302   printf ("%d functions in file\n", n_functions);
303 }
304
305 #define FLAG_ON_TREE 0x1
306 #define FLAG_FAKE 0x2
307 #define FLAG_FALL_THROUGH 0x4
308
309 static void
310 dump_bbg_file (const DBusString *contents)
311 {
312   int i;
313   long val;
314   int n_functions;
315   int n_arcs;
316   int n_blocks;
317   int n_arcs_off_tree;
318   
319   n_arcs_off_tree = 0;
320   n_blocks = 0;
321   n_arcs = 0;
322   n_functions = 0;
323   i = 0;
324   while (i < _dbus_string_get_length (contents))
325     {
326       long n_blocks_in_func;
327       long n_arcs_in_func; 
328       int j;
329
330 #ifdef DBUS_HAVE_GCC33_GCOV
331       /* In gcc33 .bbg files, there's a function name of the form:
332        *   -1, length, name (padded to 4), -1, checksum
333        * after that header on each function description, it's
334        * the same as in gcc32
335        */
336
337       {
338         DBusString funcname;
339         int checksum;
340         
341         if (!_dbus_string_init (&funcname))
342           die ("no memory\n");
343
344         if (!string_get_function (contents, i,
345                                   &funcname, &checksum, &i))
346           die ("could not read function name\n");
347         
348         printf ("Function name is \"%s\" checksum %d\n",
349                 _dbus_string_get_const_data (&funcname),
350                 checksum);
351         
352         _dbus_string_free (&funcname);
353       }
354 #endif /* DBUS_HAVE_GCC33_GCOV */
355       
356       if (!string_get_int (contents, i, &val))
357         die ("no count of blocks in func found\n");
358       
359       i += 4;
360       
361       n_blocks_in_func = val;
362
363       if (!string_get_int (contents, i, &n_arcs_in_func))
364         break;
365
366       i += 4;
367
368       printf ("Function has %ld blocks and %ld arcs\n",
369               n_blocks_in_func, n_arcs_in_func);
370
371       n_functions += 1;
372       n_blocks += n_blocks_in_func;
373       n_arcs += n_arcs_in_func;
374       
375       j = 0;
376       while (j < n_blocks_in_func)
377         {
378           long n_arcs_in_block;
379           int k;
380           
381           if (!string_get_int (contents, i, &n_arcs_in_block))
382             break;
383
384           i += 4;
385
386           printf ("  Block has %ld arcs\n", n_arcs_in_block);
387           
388           k = 0;
389           while (k < n_arcs_in_block)
390             {
391               long destination_block;
392               long flags;
393               
394               if (!string_get_int (contents, i, &destination_block))
395                 break;
396
397               i += 4;
398               
399               if (!string_get_int (contents, i, &flags))
400                 break;
401
402               i += 4;
403
404               printf ("    Arc has destination block %ld flags 0x%lx\n",
405                       destination_block, flags);
406
407               if ((flags & FLAG_ON_TREE) == 0)
408                 n_arcs_off_tree += 1;
409               
410               ++k;
411             }
412
413           if (k < n_arcs_in_block)
414             break;
415           
416           ++j;
417         }
418
419       if (j < n_blocks_in_func)
420         break;
421
422       if (!string_get_int (contents, i, &val))
423         break;
424
425       i += 4;
426
427       if (val != -1)
428         die ("-1 separator not found\n");
429     }
430
431   printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
432           n_functions, n_blocks, n_arcs, n_arcs_off_tree);
433 }
434
435 #ifndef DBUS_HAVE_GCC33_GCOV
436
437 /* gcc 3.2 version:
438  * The da file contains first a count of arcs in the file,
439  * then a count of executions for all "off tree" arcs
440  * in the file.
441  */
442 static void
443 dump_da_file (const DBusString *contents)
444 {
445   int i;
446   dbus_int64_t val;
447   int n_arcs;
448   int claimed_n_arcs;
449
450   i = 0;
451   if (!string_get_int64 (contents, i, &val))
452     return;
453
454   i += 8;
455   
456   printf ("%ld arcs in file\n", (long) val);
457   claimed_n_arcs = val;
458   
459   n_arcs = 0;
460   while (string_get_int64 (contents, i, &val))
461     {
462       i += 8;
463
464       printf ("%ld executions of arc %d\n",
465               (long) val, n_arcs);
466
467       ++n_arcs;
468     }
469
470   if (n_arcs != claimed_n_arcs)
471     {
472       printf ("File claimed to have %d arcs but only had %d\n",
473               claimed_n_arcs, n_arcs);
474     }
475 }
476
477 #else /* DBUS_HAVE_GCC33_GCOV */
478
479 /* gcc 3.3 version:
480  * The da file is more complex than 3.2.
481  *
482  * We have a magic value of "-123" only it isn't really
483  * -123, it's -123 as encoded by the crackass gcov-io.h
484  * routines. Anyway, 4 bytes.
485  *
486  * We then have:
487  *
488  *   - 4 byte count of how many functions in the following list
489  *   - 4 byte length of random extra data
490  *   - the random extra data, just skip it, info pages have some
491  *     details on what might be in there or see __bb_exit_func in gcc
492  *   - then for each function (number of functions given above):
493  *     . -1, length, funcname, alignment padding, -1
494  *     . checksum
495  *     . 4 byte number of arcs in function
496  *     . 8 bytes each, a count of execution for each arc
497  *
498  * Now, the whole thing *starting with the magic* can repeat.
499  * This is caused by multiple runs of the profiled app appending
500  * to the file.
501  */
502 static void
503 dump_da_file (const DBusString *contents)
504 {
505   int i;
506   dbus_int64_t v64;
507   long val;
508   int n_sections;
509   int total_functions;
510
511   total_functions = 0;
512   n_sections = 0;
513
514   i = 0;
515   while (i < _dbus_string_get_length (contents))
516     {
517       int claimed_n_functions;
518       int n_functions;
519       int total_arcs;
520
521       printf (".da file section %d\n", n_sections);
522       
523       if (!string_get_int (contents, i, &val))
524         die ("no magic found in .da file\n");
525
526       i += 4;
527
528       if (val != -123)
529         die ("wrong file magic in .da file\n");
530
531       if (!string_get_int (contents, i, &val))
532         die ("no function count in .da file\n");
533       i += 4;
534       claimed_n_functions = val;
535
536       printf ("%d functions expected in section %d of .da file\n",
537               claimed_n_functions, n_sections);
538       
539       if (!string_get_int (contents, i, &val))
540         die ("no extra data length in .da file\n");
541
542       i += 4;
543
544       i += val;
545
546       total_arcs = 0;
547       n_functions = 0;
548       while (n_functions < claimed_n_functions)
549         {
550           DBusString funcname;
551           int checksum;
552           int claimed_n_arcs;
553           int n_arcs;
554           
555           if (!_dbus_string_init (&funcname))
556             die ("no memory\n");
557           
558           if (!string_get_function (contents, i,
559                                     &funcname, &checksum, &i))
560             die ("could not read function name\n");
561           
562           if (!string_get_int (contents, i, &val))
563             die ("no arc count for function\n");
564           
565           i += 4;
566           claimed_n_arcs = val;
567           
568           printf ("  %d arcs in function %d %s checksum %d\n",
569                   claimed_n_arcs, n_functions,
570                   _dbus_string_get_const_data (&funcname),
571                   checksum);
572           
573           n_arcs = 0;
574           while (n_arcs < claimed_n_arcs)
575             {
576               if (!string_get_int64 (contents, i, &v64))
577                 die ("did not get execution count for arc\n");
578               
579               i += 8;
580               
581               printf ("    %ld executions of arc %d (total arcs %d)\n",
582                       (long) v64, n_arcs, total_arcs + n_arcs);
583               
584               ++n_arcs;
585             }
586
587           _dbus_string_free (&funcname);
588
589           total_arcs += n_arcs;
590           ++n_functions;
591         }
592
593       printf ("total of %d functions and %d arcs in section %d\n",
594               n_functions, total_arcs, n_sections);
595       
596       total_functions += n_functions;
597       ++n_sections;
598     }
599
600   printf ("%d total function sections in %d total .da file sections\n",
601           total_functions, n_sections);
602 }
603
604 #endif /* DBUS_HAVE_GCC33_GCOV */
605
606 typedef struct Arc Arc;
607 typedef struct Block Block;
608 typedef struct Function Function;
609 typedef struct File File;
610 typedef struct Line Line;
611
612 struct Arc
613 {
614   int source;
615   int target;
616   dbus_int64_t arc_count;
617   unsigned int count_valid : 1;
618   unsigned int on_tree : 1;
619   unsigned int fake : 1;
620   unsigned int fall_through : 1;
621   Arc *pred_next;
622   Arc *succ_next;
623 };
624
625 struct Block
626 {
627   Arc *succ;
628   Arc *pred;
629   dbus_int64_t succ_count;
630   dbus_int64_t pred_count;
631   dbus_int64_t exec_count;
632   DBusList *lines;
633   unsigned int count_valid : 1;
634   unsigned int on_tree : 1;
635   unsigned int inside_dbus_build_tests : 1;
636 };
637
638 struct Function
639 {
640   char *name;
641   int checksum;
642   Block *block_graph;
643   int n_blocks;
644   /* number of blocks in DBUS_BUILD_TESTS */
645   int n_test_blocks;
646   int n_test_blocks_executed;
647   /* number of blocks outside DBUS_BUILD_TESTS */
648   int n_nontest_blocks;
649   int n_nontest_blocks_executed;
650   /* Summary result flags */
651   unsigned int unused : 1;
652   unsigned int inside_dbus_build_tests : 1;
653   unsigned int partial : 1; /* only some of the blocks were executed */
654 };
655
656 struct Line
657 {
658   int    number;
659   char  *text;
660   DBusList *blocks;
661   unsigned int inside_dbus_build_tests : 1;
662   unsigned int partial : 1; /* only some of the blocks were executed */
663 };
664
665 struct File
666 {
667   char *name;
668   Line *lines;
669   int   n_lines;
670   DBusList *functions;
671 };
672
673 static void
674 function_add_arc (Function *function,
675                   long      source,
676                   long      target,
677                   long      flags)
678 {
679   Arc *arc;
680
681   arc = dbus_new0 (Arc, 1);
682   if (arc == NULL)
683     die ("no memory\n");
684   
685   arc->target = target;
686   arc->source = source;
687
688   arc->succ_next = function->block_graph[source].succ;
689   function->block_graph[source].succ = arc;
690   function->block_graph[source].succ_count += 1;
691
692   arc->pred_next = function->block_graph[target].pred;
693   function->block_graph[target].pred = arc;
694   function->block_graph[target].pred_count += 1;
695
696   if ((flags & FLAG_ON_TREE) != 0)
697     arc->on_tree = TRUE;
698
699   if ((flags & FLAG_FAKE) != 0)
700     arc->fake = TRUE;
701
702   if ((flags & FLAG_FALL_THROUGH) != 0)
703     arc->fall_through = TRUE;
704 }
705
706
707 static Arc*
708 reverse_arcs (Arc *arc)
709 {
710   struct Arc *prev = 0;
711   struct Arc *next;
712
713   for ( ; arc; arc = next)
714     {
715       next = arc->succ_next;
716       arc->succ_next = prev;
717       prev = arc;
718     }
719
720   return prev;
721 }
722
723 static void
724 function_reverse_succ_arcs (Function *func)
725 {
726   /* Must reverse the order of all succ arcs, to ensure that they match
727    * the order of the data in the .da file.
728    */
729   int i;
730   
731   for (i = 0; i < func->n_blocks; i++)
732     if (func->block_graph[i].succ)
733       func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ);
734 }
735
736 static void
737 get_functions_from_bbg (const DBusString  *contents,
738                         DBusList         **functions)
739 {
740   int i;
741   long val;
742   int n_functions;
743   int n_arcs;
744   int n_blocks;
745   int n_arcs_off_tree;
746
747 #if 0
748   printf ("Loading arcs and blocks from .bbg file\n");
749 #endif
750   
751   n_arcs_off_tree = 0;
752   n_blocks = 0;
753   n_arcs = 0;
754   n_functions = 0;
755   i = 0;
756   while (i < _dbus_string_get_length (contents))
757     {
758       Function *func;
759       long n_blocks_in_func;
760       long n_arcs_in_func; 
761       int j;
762
763 #ifdef DBUS_HAVE_GCC33_GCOV
764       DBusString funcname;
765       int checksum;
766
767       /* In gcc33 .bbg files, there's a function name of the form:
768        *   -1, length, name (padded to 4), -1, checksum
769        * after that header on each function description, it's
770        * the same as in gcc32
771        */
772       if (!_dbus_string_init (&funcname))
773         die ("no memory\n");
774       
775       if (!string_get_function (contents, i,
776                                 &funcname, &checksum, &i))
777         die ("could not read function name\n");
778 #endif /* DBUS_HAVE_GCC33_GCOV */
779
780       if (!string_get_int (contents, i, &val))
781         break;
782       
783       n_blocks_in_func = val;
784       
785       i += 4;
786
787       if (!string_get_int (contents, i, &n_arcs_in_func))
788         break;
789
790       i += 4;
791
792       n_functions += 1;
793       n_blocks += n_blocks_in_func;
794       n_arcs += n_arcs_in_func;
795
796       func = dbus_new0 (Function, 1);
797       if (func == NULL)
798         die ("no memory\n");
799
800 #ifdef DBUS_HAVE_GCC33_GCOV
801       func->name = _dbus_strdup (_dbus_string_get_const_data (&funcname));
802       func->checksum = checksum;
803       _dbus_string_free (&funcname);
804 #endif
805       
806       func->block_graph = dbus_new0 (Block, n_blocks_in_func);
807       func->n_blocks = n_blocks_in_func;
808       
809       j = 0;
810       while (j < n_blocks_in_func)
811         {
812           long n_arcs_in_block;
813           int k;
814           
815           if (!string_get_int (contents, i, &n_arcs_in_block))
816             break;
817
818           i += 4;
819           
820           k = 0;
821           while (k < n_arcs_in_block)
822             {
823               long destination_block;
824               long flags;
825               
826               if (!string_get_int (contents, i, &destination_block))
827                 break;
828
829               i += 4;
830               
831               if (!string_get_int (contents, i, &flags))
832                 break;
833
834               i += 4;
835
836               if ((flags & FLAG_ON_TREE) == 0)
837                 n_arcs_off_tree += 1;
838
839               function_add_arc (func, j, destination_block,
840                                 flags);
841               
842               ++k;
843             }
844
845           if (k < n_arcs_in_block)
846             break;
847           
848           ++j;
849         }
850
851       if (j < n_blocks_in_func)
852         break;
853
854       function_reverse_succ_arcs (func);
855       
856       if (!_dbus_list_append (functions, func))
857         die ("no memory\n");
858       
859       if (!string_get_int (contents, i, &val))
860         break;
861
862       i += 4;
863
864       if (val != -1)
865         die ("-1 separator not found in .bbg file\n");
866     }
867
868 #if 0
869   printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
870           n_functions, n_blocks, n_arcs, n_arcs_off_tree);
871 #endif
872   
873   _dbus_assert (n_functions == _dbus_list_get_length (functions));
874 }
875
876 #ifdef DBUS_HAVE_GCC33_GCOV
877 static void
878 add_counts_from_da (const DBusString  *contents,
879                     DBusList         **functions)
880 {
881   int i;
882   dbus_int64_t v64;
883   long val;
884   int n_sections;
885   DBusList *link;
886   Function *current_func;  
887   int current_block;
888   Arc *current_arc;
889
890   n_sections = 0;
891
892   i = 0;
893   while (i < _dbus_string_get_length (contents))
894     {
895       int claimed_n_functions;
896       int n_functions;
897       
898       if (!string_get_int (contents, i, &val))
899         die ("no magic found in .da file\n");
900
901       i += 4;
902
903       if (val != -123)
904         die ("wrong file magic in .da file\n");
905
906       if (!string_get_int (contents, i, &val))
907         die ("no function count in .da file\n");
908       i += 4;
909       claimed_n_functions = val;
910       
911       if (!string_get_int (contents, i, &val))
912         die ("no extra data length in .da file\n");
913
914       i += 4;
915
916       i += val;
917
918       link = _dbus_list_get_first_link (functions);
919       if (link == NULL)
920         goto no_more_functions;
921       
922       n_functions = 0;
923       while (n_functions < claimed_n_functions && link != NULL)
924         {
925           DBusString funcname;
926           int checksum;
927           int claimed_n_arcs;
928           int n_arcs;
929
930           current_func = link->data;
931           current_block = 0;
932           current_arc = current_func->block_graph[current_block].succ;
933           
934           if (!_dbus_string_init (&funcname))
935             die ("no memory\n");
936           
937           if (!string_get_function (contents, i,
938                                     &funcname, &checksum, &i))
939             die ("could not read function name\n");
940
941           if (!_dbus_string_equal_c_str (&funcname, current_func->name))
942             {
943               fprintf (stderr, "Expecting .da info for %s but got %s\n",
944                        current_func->name,
945                        _dbus_string_get_const_data (&funcname));
946               exit (1);
947             }
948           
949           if (checksum != current_func->checksum)
950             die (".da file checksum doesn't match checksum from .bbg file\n");
951           
952           if (!string_get_int (contents, i, &val))
953             die ("no arc count for function\n");
954           
955           i += 4;
956           claimed_n_arcs = val;
957
958           /* For each arc in the profile, find the corresponding
959            * arc in the function and increment its count
960            */
961           n_arcs = 0;
962           while (n_arcs < claimed_n_arcs)
963             {
964               if (!string_get_int64 (contents, i, &v64))
965                 die ("did not get execution count for arc\n");
966               
967               i += 8;
968
969               /* Find the next arc in the function that isn't on tree */
970               while (current_arc == NULL ||
971                      current_arc->on_tree)
972                 {
973                   if (current_arc == NULL)
974                     {
975                       ++current_block;
976               
977                       if (current_block >= current_func->n_blocks)
978                         die ("too many blocks in function\n");
979               
980                       current_arc = current_func->block_graph[current_block].succ;
981                     }
982                   else
983                     {
984                       current_arc = current_arc->succ_next;
985                     }
986                 }
987               
988               _dbus_assert (current_arc != NULL);
989               _dbus_assert (!current_arc->on_tree);
990               
991               current_arc->arc_count = v64;
992               current_arc->count_valid = TRUE;
993               current_func->block_graph[current_block].succ_count -= 1;
994               current_func->block_graph[current_arc->target].pred_count -= 1;
995               
996               ++n_arcs;
997               
998               current_arc = current_arc->succ_next;
999             }
1000
1001           _dbus_string_free (&funcname);
1002
1003           link = _dbus_list_get_next_link (functions, link);
1004           ++n_functions;
1005
1006           if (link == NULL && n_functions < claimed_n_functions)
1007             {
1008               fprintf (stderr, "Ran out of functions loading .da file\n");
1009               goto no_more_functions;
1010             }
1011         }
1012
1013     no_more_functions:
1014       
1015       ++n_sections;
1016     }
1017 }
1018 #else /* DBUS_HAVE_GCC33_GCOV */
1019 static void
1020 add_counts_from_da (const DBusString  *contents,
1021                     DBusList         **functions)
1022 {
1023   int i;
1024   dbus_int64_t val;
1025   int n_arcs;
1026   int claimed_n_arcs;
1027   DBusList *link;
1028   Function *current_func;  
1029   int current_block;
1030   Arc *current_arc;
1031
1032 #if 0
1033   printf ("Loading execution count for each arc from .da file\n");
1034 #endif
1035   
1036   i = 0;
1037   if (!string_get_int64 (contents, i, &val))
1038     return;
1039
1040   i += 8;
1041   
1042   claimed_n_arcs = val;
1043
1044   link = _dbus_list_get_first_link (functions);
1045   if (link == NULL)
1046     goto done;
1047
1048   current_func = link->data;
1049   current_block = 0;
1050   current_arc = current_func->block_graph[current_block].succ;
1051   
1052   n_arcs = 0;
1053   while (string_get_int64 (contents, i, &val))
1054     {
1055       i += 8;
1056
1057       while (current_arc == NULL ||
1058              current_arc->on_tree)
1059         {
1060           if (current_arc == NULL)
1061             {
1062               ++current_block;
1063               
1064               if (current_block == current_func->n_blocks)
1065                 {
1066                   link = _dbus_list_get_next_link (functions, link);
1067                   if (link == NULL)
1068                     {
1069                       fprintf (stderr, "Ran out of functions loading .da file\n");
1070                       goto done;
1071                     }
1072                   current_func = link->data;
1073                   current_block = 0;
1074                 }
1075               
1076               current_arc = current_func->block_graph[current_block].succ;
1077             }
1078           else
1079             {
1080               current_arc = current_arc->succ_next;
1081             }
1082         }
1083
1084       _dbus_assert (current_arc != NULL);
1085       _dbus_assert (!current_arc->on_tree);
1086
1087       current_arc->arc_count = val;
1088       current_arc->count_valid = TRUE;
1089       current_func->block_graph[current_block].succ_count -= 1;
1090       current_func->block_graph[current_arc->target].pred_count -= 1;
1091       
1092       ++n_arcs;
1093
1094       current_arc = current_arc->succ_next;
1095     }
1096
1097  done:
1098   
1099   if (n_arcs != claimed_n_arcs)
1100     {
1101       fprintf (stderr, "File claimed to have %d arcs but only had %d\n",
1102                claimed_n_arcs, n_arcs);
1103       exit (1);
1104     }
1105
1106 #if 0
1107   printf ("%d arcs in file\n", n_arcs);
1108 #endif
1109 }
1110 #endif
1111
1112 static void
1113 function_solve_graph (Function *func)
1114 {
1115   int passes, changes;
1116   dbus_int64_t total;
1117   int i;
1118   Arc *arc;
1119   Block *block_graph;
1120   int n_blocks;
1121
1122 #if 0
1123   printf ("Solving function graph\n");
1124 #endif
1125   
1126   n_blocks = func->n_blocks;
1127   block_graph = func->block_graph;
1128
1129   /* For every block in the file,
1130      - if every exit/entrance arc has a known count, then set the block count
1131      - if the block count is known, and every exit/entrance arc but one has
1132      a known execution count, then set the count of the remaining arc
1133
1134      As arc counts are set, decrement the succ/pred count, but don't delete
1135      the arc, that way we can easily tell when all arcs are known, or only
1136      one arc is unknown.  */
1137
1138   /* The order that the basic blocks are iterated through is important.
1139      Since the code that finds spanning trees starts with block 0, low numbered
1140      arcs are put on the spanning tree in preference to high numbered arcs.
1141      Hence, most instrumented arcs are at the end.  Graph solving works much
1142      faster if we propagate numbers from the end to the start.
1143
1144      This takes an average of slightly more than 3 passes.  */
1145
1146   changes = 1;
1147   passes = 0;
1148   while (changes)
1149     {
1150       passes++;
1151       changes = 0;
1152
1153       for (i = n_blocks - 1; i >= 0; i--)
1154         {
1155           if (! block_graph[i].count_valid)
1156             {
1157               if (block_graph[i].succ_count == 0)
1158                 {
1159                   total = 0;
1160                   for (arc = block_graph[i].succ; arc;
1161                        arc = arc->succ_next)
1162                     total += arc->arc_count;
1163                   block_graph[i].exec_count = total;
1164                   block_graph[i].count_valid = 1;
1165                   changes = 1;
1166                 }
1167               else if (block_graph[i].pred_count == 0)
1168                 {
1169                   total = 0;
1170                   for (arc = block_graph[i].pred; arc;
1171                        arc = arc->pred_next)
1172                     total += arc->arc_count;
1173                   block_graph[i].exec_count = total;
1174                   block_graph[i].count_valid = 1;
1175                   changes = 1;
1176                 }
1177             }
1178           if (block_graph[i].count_valid)
1179             {
1180               if (block_graph[i].succ_count == 1)
1181                 {
1182                   total = 0;
1183                   /* One of the counts will be invalid, but it is zero,
1184                      so adding it in also doesn't hurt.  */
1185                   for (arc = block_graph[i].succ; arc;
1186                        arc = arc->succ_next)
1187                     total += arc->arc_count;
1188                   /* Calculate count for remaining arc by conservation.  */
1189                   total = block_graph[i].exec_count - total;
1190                   /* Search for the invalid arc, and set its count.  */
1191                   for (arc = block_graph[i].succ; arc;
1192                        arc = arc->succ_next)
1193                     if (! arc->count_valid)
1194                       break;
1195                   if (! arc)
1196                     die ("arc == NULL\n");
1197                   arc->count_valid = 1;
1198                   arc->arc_count = total;
1199                   block_graph[i].succ_count -= 1;
1200
1201                   block_graph[arc->target].pred_count -= 1;
1202                   changes = 1;
1203                 }
1204               if (block_graph[i].pred_count == 1)
1205                 {
1206                   total = 0;
1207                   /* One of the counts will be invalid, but it is zero,
1208                      so adding it in also doesn't hurt.  */
1209                   for (arc = block_graph[i].pred; arc;
1210                        arc = arc->pred_next)
1211                     total += arc->arc_count;
1212                   /* Calculate count for remaining arc by conservation.  */
1213                   total = block_graph[i].exec_count - total;
1214                   /* Search for the invalid arc, and set its count.  */
1215                   for (arc = block_graph[i].pred; arc;
1216                        arc = arc->pred_next)
1217                     if (! arc->count_valid)
1218                       break;
1219                   if (! arc)
1220                     die ("arc == NULL\n");
1221                   arc->count_valid = 1;
1222                   arc->arc_count = total;
1223                   block_graph[i].pred_count -= 1;
1224
1225                   block_graph[arc->source].succ_count -= 1;
1226                   changes = 1;
1227                 }
1228             }
1229         }
1230     }
1231
1232   /* If the graph has been correctly solved, every block will have a
1233    * succ and pred count of zero.
1234    */
1235   {
1236     dbus_bool_t header = FALSE;
1237     for (i = 0; i < n_blocks; i++)
1238       {
1239         if (block_graph[i].succ_count || block_graph[i].pred_count)
1240           {
1241             if (!header)
1242               {
1243                 fprintf (stderr, "WARNING: Block graph solved incorrectly for function %s\n",
1244                          func->name);
1245                 fprintf (stderr, " this error reflects a bug in decode-gcov.c or perhaps bogus data\n");
1246                 header = TRUE;
1247               }
1248             fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n",
1249                      i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count);
1250           }
1251       }
1252   }
1253 }
1254
1255 static void
1256 solve_graphs (DBusList **functions)
1257 {
1258   DBusList *link;
1259
1260   link = _dbus_list_get_first_link (functions);
1261   while (link != NULL)
1262     {
1263       Function *func = link->data;
1264
1265       function_solve_graph (func);
1266       
1267       link = _dbus_list_get_next_link (functions, link);
1268     }
1269 }
1270
1271 static void
1272 load_functions_for_c_file (const DBusString *filename,
1273                            DBusList        **functions)
1274 {
1275   DBusString bbg_filename;
1276   DBusString da_filename;
1277   DBusString contents;
1278   DBusError error;
1279
1280   dbus_error_init (&error);
1281   
1282   if (!_dbus_string_init (&bbg_filename) ||
1283       !_dbus_string_init (&da_filename) ||
1284       !_dbus_string_copy (filename, 0, &bbg_filename, 0) ||
1285       !_dbus_string_copy (filename, 0, &da_filename, 0) ||
1286       !_dbus_string_init (&contents))
1287     die ("no memory\n");
1288
1289   _dbus_string_shorten (&bbg_filename, 2);
1290   _dbus_string_shorten (&da_filename, 2);
1291
1292   if (!_dbus_string_append (&bbg_filename, ".bbg") ||
1293       !_dbus_string_append (&da_filename, ".da"))
1294     die ("no memory\n");
1295       
1296   if (!_dbus_file_get_contents (&contents, &bbg_filename,
1297                                 &error))
1298     {
1299       fprintf (stderr, "Could not open file: %s\n",
1300                error.message);
1301       exit (1);
1302     }
1303
1304   get_functions_from_bbg (&contents, functions);
1305
1306   _dbus_string_set_length (&contents, 0);
1307
1308   if (!_dbus_file_get_contents (&contents, &da_filename,
1309                                 &error))
1310     {
1311       /* Try .libs/file.da */
1312       int slash;
1313
1314       if (_dbus_string_find_byte_backward (&da_filename,
1315                                             _dbus_string_get_length (&da_filename),
1316                                             '/',
1317                                             &slash))
1318         {
1319           DBusString libs;
1320           _dbus_string_init_const (&libs, "/.libs");
1321
1322           if (!_dbus_string_copy (&libs, 0, &da_filename, slash))
1323             die ("no memory");
1324
1325           dbus_error_free (&error);
1326           if (!_dbus_file_get_contents (&contents, &da_filename,
1327                                         &error))
1328             {
1329               fprintf (stderr, "Could not open file: %s\n",
1330                        error.message);
1331               exit (1);
1332             }
1333         }
1334       else
1335         {
1336           fprintf (stderr, "Could not open file: %s\n",
1337                    error.message);
1338           exit (1);
1339         }
1340     }
1341   
1342   add_counts_from_da (&contents, functions);
1343   
1344   solve_graphs (functions);
1345
1346   _dbus_string_free (&contents);
1347   _dbus_string_free (&da_filename);
1348   _dbus_string_free (&bbg_filename);
1349 }
1350
1351 static void
1352 get_lines_from_bb_file (const DBusString *contents,
1353                         File             *fl)
1354 {
1355   int i;
1356   long val;
1357   int n_functions;
1358   dbus_bool_t in_our_file;
1359   DBusList *link;
1360   Function *func;
1361   int block;
1362
1363 #if 0
1364   printf ("Getting line numbers for blocks from .bb file\n");
1365 #endif
1366   
1367   /* There's this "filename" field in the .bb file which
1368    * mysteriously comes *after* the first function in the
1369    * file in the .bb file; and every .bb file seems to
1370    * have only one filename. I don't understand
1371    * what's going on here, so just set in_our_file = TRUE
1372    * at the start categorically.
1373    */
1374   
1375   block = 0;
1376   func = NULL;
1377   in_our_file = TRUE;
1378   link = _dbus_list_get_first_link (&fl->functions);
1379   n_functions = 0;
1380   i = 0;
1381   while (string_get_int (contents, i, &val))
1382     {
1383       i += 4;
1384       
1385       switch (val)
1386         {
1387         case BB_FILENAME:
1388           {
1389             DBusString f;
1390
1391             if (!_dbus_string_init (&f))
1392               die ("no memory\n");
1393
1394             if (string_get_string (contents, i,
1395                                    BB_FILENAME,
1396                                    &f, &i))
1397               {
1398                 /* fl->name is a full path and the filename in .bb is
1399                  * not.
1400                  */
1401                 DBusString tmp_str;
1402
1403                 _dbus_string_init_const (&tmp_str, fl->name);
1404                 
1405                 if (_dbus_string_ends_with_c_str (&tmp_str,
1406                                                   _dbus_string_get_const_data (&f)))
1407                   in_our_file = TRUE;
1408                 else
1409                   in_our_file = FALSE;
1410                 
1411 #if 0
1412                 fprintf (stderr,
1413                          "File %s in .bb, looking for %s, in_our_file = %d\n",
1414                          _dbus_string_get_const_data (&f),
1415                          fl->name,
1416                          in_our_file);
1417 #endif
1418               }
1419             _dbus_string_free (&f);
1420           }
1421           break;
1422         case BB_FUNCTION:
1423           {
1424             DBusString f;
1425             if (!_dbus_string_init (&f))
1426               die ("no memory\n");
1427
1428             if (string_get_string (contents, i,
1429                                    BB_FUNCTION,
1430                                    &f, &i))
1431               {
1432 #if 0
1433                 fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f));
1434 #endif
1435
1436                 block = 0;
1437                 
1438                 if (in_our_file)
1439                   {
1440                     if (link == NULL)
1441                       {
1442                         fprintf (stderr, "No function object for function %s\n",
1443                                  _dbus_string_get_const_data (&f));
1444                       }
1445                     else
1446                       {
1447                         func = link->data;
1448                         link = _dbus_list_get_next_link (&fl->functions, link);
1449
1450                         if (func->name == NULL)
1451                           {
1452                             if (!_dbus_string_copy_data (&f, &func->name))
1453                               die ("no memory\n");
1454                           }
1455                         else
1456                           {
1457                             if (!_dbus_string_equal_c_str (&f, func->name))
1458                               {
1459                                 fprintf (stderr, "got function name \"%s\" (%d) from .bbg file, but \"%s\" (%d) from .bb file\n",
1460                                          func->name, strlen (func->name),
1461                                          _dbus_string_get_const_data (&f),
1462                                          _dbus_string_get_length (&f));
1463
1464                               }
1465                           }
1466                       }
1467                   }
1468               }
1469             _dbus_string_free (&f);
1470
1471             n_functions += 1;
1472           }
1473           break;
1474         case BB_ENDOFLIST:
1475           block += 1;
1476           break;
1477         default:
1478 #if 0
1479           fprintf (stderr, "Line %ld\n", val);
1480 #endif
1481
1482           if (val >= fl->n_lines)
1483             {
1484               fprintf (stderr, "Line %ld but file only has %d lines\n",
1485                        val, fl->n_lines);
1486             }
1487           else if (func != NULL)
1488             {
1489               val -= 1; /* To convert the 1-based line number to 0-based */
1490               _dbus_assert (val >= 0);
1491               
1492               if (block < func->n_blocks)
1493                 {
1494                   if (!_dbus_list_append (&func->block_graph[block].lines,
1495                                           &fl->lines[val]))
1496                     die ("no memory\n");
1497                   
1498                   
1499                   if (!_dbus_list_append (&fl->lines[val].blocks,
1500                                           &func->block_graph[block]))
1501                     die ("no memory\n");
1502                 }
1503               else
1504                 {
1505                   fprintf (stderr, "Line number for block %d but function only has %d blocks\n",
1506                            block, func->n_blocks);
1507                 }
1508             }
1509           else
1510             {
1511               fprintf (stderr, "Line %ld given outside of any function\n",
1512                        val);
1513             }
1514           
1515           break;
1516         }
1517     }
1518
1519 #if 0
1520   printf ("%d functions in file\n", n_functions);
1521 #endif
1522 }
1523
1524
1525 static void
1526 load_block_line_associations (const DBusString *filename,
1527                               File             *f)
1528 {
1529   DBusString bb_filename;
1530   DBusString contents;
1531   DBusError error;
1532
1533   dbus_error_init (&error);
1534   
1535   if (!_dbus_string_init (&bb_filename) ||
1536       !_dbus_string_copy (filename, 0, &bb_filename, 0) ||
1537       !_dbus_string_init (&contents))
1538     die ("no memory\n");
1539
1540   _dbus_string_shorten (&bb_filename, 2);
1541
1542   if (!_dbus_string_append (&bb_filename, ".bb"))
1543     die ("no memory\n");
1544       
1545   if (!_dbus_file_get_contents (&contents, &bb_filename,
1546                                 &error))
1547     {
1548       fprintf (stderr, "Could not open file: %s\n",
1549                error.message);
1550       exit (1);
1551     }
1552   
1553   get_lines_from_bb_file (&contents, f);
1554
1555   _dbus_string_free (&contents);
1556   _dbus_string_free (&bb_filename);
1557 }
1558
1559 static int
1560 count_lines_in_string (const DBusString *str)
1561 {
1562   int n_lines;
1563   const char *p;
1564   const char *prev;
1565   const char *end;
1566   const char *last_line_end;
1567
1568 #if 0
1569   printf ("Counting lines in source file\n");
1570 #endif
1571   
1572   n_lines = 0;  
1573   prev = NULL;
1574   p = _dbus_string_get_const_data (str);
1575   end = p + _dbus_string_get_length (str);
1576   last_line_end = p;
1577   while (p != end)
1578     {
1579       /* too lazy to handle \r\n as one linebreak */
1580       if (*p == '\n' || *p == '\r')
1581         {
1582           ++n_lines;
1583           last_line_end = p + 1;
1584         }
1585
1586       prev = p;
1587       ++p;
1588     }
1589
1590   if (last_line_end != p)
1591     ++n_lines;
1592   
1593   return n_lines;
1594 }
1595
1596 static void
1597 fill_line_content (const DBusString *str,
1598                    Line             *lines)
1599 {
1600   int n_lines;
1601   const char *p;
1602   const char *prev;
1603   const char *end;
1604   const char *last_line_end;
1605
1606 #if 0
1607   printf ("Saving contents of each line in source file\n");
1608 #endif
1609   
1610   n_lines = 0;
1611   prev = NULL;
1612   p = _dbus_string_get_const_data (str);
1613   end = p + _dbus_string_get_length (str);
1614   last_line_end = p;
1615   while (p != end)
1616     {
1617       if (*p == '\n' || *p == '\r')
1618         {
1619           lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1);
1620           if (lines[n_lines].text == NULL)
1621             die ("no memory\n");
1622
1623           memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1624           lines[n_lines].number = n_lines + 1;
1625           
1626           ++n_lines;
1627
1628           last_line_end = p + 1;
1629         }
1630
1631       prev = p;
1632       ++p;
1633     }
1634
1635   if (p != last_line_end)
1636     {
1637       memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1638       ++n_lines;
1639     }
1640 }
1641
1642 static void
1643 mark_inside_dbus_build_tests (File  *f)
1644 {
1645   int i;
1646   DBusList *link;
1647   int inside_depth;
1648
1649   inside_depth = 0;
1650   i = 0;
1651   while (i < f->n_lines)
1652     {
1653       Line *l = &f->lines[i];
1654       dbus_bool_t is_verbose;
1655
1656       is_verbose = strstr (l->text, "_dbus_verbose") != NULL;
1657
1658       if (inside_depth == 0)
1659         {
1660           const char *a, *b;
1661           
1662           a = strstr (l->text, "#if");
1663           b = strstr (l->text, "DBUS_BUILD_TESTS");
1664           if (a && b && (a < b))
1665             inside_depth += 1;
1666         }
1667       else
1668         {
1669           if (strstr (l->text, "#if") != NULL)
1670             inside_depth += 1;
1671           else if (strstr (l->text, "#endif") != NULL)
1672             inside_depth -= 1;
1673         }
1674
1675       if (inside_depth > 0 || is_verbose)
1676         {
1677           /* Mark the line and its blocks */
1678           DBusList *blink;
1679
1680           l->inside_dbus_build_tests = TRUE;
1681           
1682           blink = _dbus_list_get_first_link (&l->blocks);
1683           while (blink != NULL)
1684             {
1685               Block *b = blink->data;
1686
1687               b->inside_dbus_build_tests = TRUE;
1688               
1689               blink = _dbus_list_get_next_link (&l->blocks, blink);
1690             }
1691         }
1692       
1693       ++i;
1694     }
1695
1696   /* Now mark functions where for all blocks that are associated
1697    * with a source line, the block is inside_dbus_build_tests.
1698    */
1699   link = _dbus_list_get_first_link (&f->functions);
1700   while (link != NULL)
1701     {
1702       Function *func = link->data;
1703
1704       /* The issue is that some blocks aren't associated with a source line.
1705        * Assume they are inside/outside tests according to the source
1706        * line of the preceding block. For the first block, make it
1707        * match the first following block with a line associated.
1708        */
1709       if (func->block_graph[0].lines == NULL)
1710         {
1711           /* find first following line */
1712           i = 1;
1713           while (i < func->n_blocks)
1714             {
1715               if (func->block_graph[i].lines != NULL)
1716                 {
1717                   func->block_graph[0].inside_dbus_build_tests =
1718                     func->block_graph[i].inside_dbus_build_tests;
1719                   break;
1720                 }
1721               
1722               ++i;
1723             }
1724         }
1725
1726       /* Now mark all blocks but the first */
1727       i = 1;
1728       while (i < func->n_blocks)
1729         {
1730           if (func->block_graph[i].lines == NULL)
1731             {
1732               func->block_graph[i].inside_dbus_build_tests =
1733                 func->block_graph[i-1].inside_dbus_build_tests;
1734             }
1735           
1736           ++i;
1737         }
1738       
1739       i = 0;
1740       while (i < func->n_blocks)
1741         {
1742           /* Break as soon as any block is not a test block */
1743           if (func->block_graph[i].lines != NULL &&
1744               !func->block_graph[i].inside_dbus_build_tests)
1745             break;
1746           
1747           ++i;
1748         }
1749
1750       if (i == func->n_blocks)
1751         func->inside_dbus_build_tests = TRUE;
1752       
1753       link = _dbus_list_get_next_link (&f->functions, link);
1754     } 
1755 }
1756
1757 static void
1758 mark_coverage (File  *f)
1759 {
1760   int i;
1761   DBusList *link;
1762   
1763   i = 0;
1764   while (i < f->n_lines)
1765     {
1766       Line *l = &f->lines[i];
1767       DBusList *blink;
1768       int n_blocks;
1769       int n_blocks_executed;
1770
1771       n_blocks = 0;
1772       n_blocks_executed = 0;
1773       blink = _dbus_list_get_first_link (&l->blocks);
1774       while (blink != NULL)
1775         {
1776           Block *b = blink->data;
1777           
1778           if (b->exec_count > 0)
1779             n_blocks_executed += 1;
1780
1781           n_blocks += 1;
1782           
1783           blink = _dbus_list_get_next_link (&l->blocks, blink);
1784         }
1785
1786       if (n_blocks_executed > 0 &&
1787           n_blocks_executed < n_blocks)
1788         l->partial = TRUE;
1789
1790       ++i;
1791     }
1792
1793   link = _dbus_list_get_first_link (&f->functions);
1794   while (link != NULL)
1795     {
1796       Function *func = link->data;
1797       int i;
1798       int n_test_blocks;
1799       int n_test_blocks_executed;
1800       int n_nontest_blocks;
1801       int n_nontest_blocks_executed;
1802       
1803       n_test_blocks = 0;
1804       n_test_blocks_executed = 0;
1805       n_nontest_blocks = 0;
1806       n_nontest_blocks_executed = 0;      
1807
1808       i = 0;
1809       while (i < func->n_blocks)
1810         {
1811           if (!func->block_graph[i].inside_dbus_build_tests)
1812             {
1813               n_nontest_blocks += 1;
1814
1815               if (func->block_graph[i].exec_count > 0)
1816                 n_nontest_blocks_executed += 1;
1817             }
1818           else
1819             {
1820               n_test_blocks += 1;
1821
1822               if (func->block_graph[i].exec_count > 0)
1823                 n_test_blocks_executed += 1;
1824             }
1825
1826           ++i;
1827         }
1828       
1829       if (n_nontest_blocks_executed > 0 &&
1830           n_nontest_blocks_executed < n_nontest_blocks)
1831         func->partial = TRUE;
1832
1833       if (n_nontest_blocks_executed == 0 &&
1834           n_nontest_blocks > 0)
1835         func->unused = TRUE;
1836       
1837       func->n_test_blocks = n_test_blocks;
1838       func->n_test_blocks_executed = n_test_blocks_executed;
1839       func->n_nontest_blocks = n_nontest_blocks;
1840       func->n_nontest_blocks_executed = n_nontest_blocks_executed;
1841       
1842       link = _dbus_list_get_next_link (&f->functions, link);
1843     }
1844 }
1845
1846 static File*
1847 load_c_file (const DBusString *filename)
1848 {
1849   DBusString contents;
1850   DBusError error;
1851   File *f;
1852   
1853   f = dbus_new0 (File, 1);
1854   if (f == NULL)
1855     die ("no memory\n");
1856
1857   if (!_dbus_string_copy_data (filename, &f->name))
1858     die ("no memory\n");
1859   
1860   if (!_dbus_string_init (&contents))
1861     die ("no memory\n");
1862       
1863   dbus_error_init (&error);
1864
1865   if (!_dbus_file_get_contents (&contents, filename,
1866                                 &error))
1867     {
1868       fprintf (stderr, "Could not open file: %s\n",
1869                error.message);
1870       dbus_error_free (&error);
1871       exit (1);
1872     }
1873       
1874   load_functions_for_c_file (filename, &f->functions);
1875
1876   f->n_lines = count_lines_in_string (&contents);
1877   f->lines = dbus_new0 (Line, f->n_lines);
1878   if (f->lines == NULL)
1879     die ("no memory\n");
1880
1881   fill_line_content (&contents, f->lines);
1882   
1883   _dbus_string_free (&contents);
1884
1885   load_block_line_associations (filename, f);
1886
1887   mark_inside_dbus_build_tests (f);
1888   mark_coverage (f);
1889   
1890   return f;
1891 }
1892
1893 typedef struct Stats Stats;
1894
1895 struct Stats
1896 {
1897   int n_blocks;
1898   int n_blocks_executed;
1899   int n_blocks_inside_dbus_build_tests;
1900   
1901   int n_lines; /* lines that have blocks on them */
1902   int n_lines_executed;
1903   int n_lines_partial;
1904   int n_lines_inside_dbus_build_tests;
1905   
1906   int n_functions;
1907   int n_functions_executed;
1908   int n_functions_partial;
1909   int n_functions_inside_dbus_build_tests;
1910 };
1911
1912 static dbus_bool_t
1913 line_was_executed (Line *l)
1914 {
1915   DBusList *link;
1916
1917   link = _dbus_list_get_first_link (&l->blocks);
1918   while (link != NULL)
1919     {
1920       Block *b = link->data;
1921
1922       if (b->exec_count > 0)
1923         return TRUE;
1924       
1925       link = _dbus_list_get_next_link (&l->blocks, link);
1926     }
1927
1928   return FALSE;
1929 }
1930
1931
1932 static int
1933 line_exec_count (Line *l)
1934 {
1935   DBusList *link;
1936   dbus_int64_t total;
1937
1938   total = 0;
1939   link = _dbus_list_get_first_link (&l->blocks);
1940   while (link != NULL)
1941     {
1942       Block *b = link->data;
1943
1944       total += b->exec_count;
1945       
1946       link = _dbus_list_get_next_link (&l->blocks, link);
1947     }
1948
1949   return total;
1950 }
1951
1952 static void
1953 merge_stats_for_file (Stats *stats,
1954                       File  *f)
1955 {
1956   int i;
1957   DBusList *link;
1958   
1959   for (i = 0; i < f->n_lines; ++i)
1960     {
1961       Line *l = &f->lines[i];
1962       
1963       if (l->inside_dbus_build_tests)
1964         {
1965           stats->n_lines_inside_dbus_build_tests += 1;
1966           continue;
1967         }
1968       
1969       if (line_was_executed (l))
1970         stats->n_lines_executed += 1;
1971
1972       if (l->blocks != NULL)
1973         stats->n_lines += 1;
1974
1975       if (l->partial)
1976         stats->n_lines_partial += 1;
1977     }
1978
1979   link = _dbus_list_get_first_link (&f->functions);
1980   while (link != NULL)
1981     {
1982       Function *func = link->data;
1983
1984       if (func->inside_dbus_build_tests)
1985         stats->n_functions_inside_dbus_build_tests += 1;
1986       else
1987         {
1988           stats->n_functions += 1;
1989
1990           if (!func->unused)
1991             stats->n_functions_executed += 1;
1992           
1993           if (func->partial)
1994             stats->n_functions_partial += 1;
1995         }
1996
1997       stats->n_blocks_inside_dbus_build_tests +=
1998         func->n_test_blocks;
1999       
2000       stats->n_blocks_executed +=
2001         func->n_nontest_blocks_executed;
2002       
2003       stats->n_blocks +=
2004         func->n_nontest_blocks;
2005
2006       link = _dbus_list_get_next_link (&f->functions, link);
2007     }
2008 }
2009
2010 /* The output of this matches gcov exactly ("diff" shows no difference) */
2011 static void
2012 print_annotated_source_gcov_format (File *f)
2013 {
2014   int i;
2015   
2016   i = 0;
2017   while (i < f->n_lines)
2018     {
2019       Line *l = &f->lines[i];
2020
2021       if (l->blocks != NULL)
2022         {
2023           int exec_count;
2024           
2025           exec_count = line_exec_count (l);
2026           
2027           if (exec_count > 0)
2028             printf ("%12d    %s\n",
2029                     exec_count, l->text);
2030           else
2031             printf ("      ######    %s\n", l->text);
2032         }
2033       else
2034         {
2035           printf ("\t\t%s\n", l->text);
2036         }
2037           
2038       ++i;
2039     }
2040 }
2041
2042 static void
2043 print_annotated_source (File *f)
2044 {
2045   int i;
2046   
2047   i = 0;
2048   while (i < f->n_lines)
2049     {
2050       Line *l = &f->lines[i];
2051
2052       if (l->inside_dbus_build_tests)
2053         printf ("*");
2054       else
2055         printf (" ");
2056       
2057       if (l->blocks != NULL)
2058         {
2059           int exec_count;
2060           
2061           exec_count = line_exec_count (l);
2062           
2063           if (exec_count > 0)
2064             printf ("%12d    %s\n",
2065                     exec_count, l->text);
2066           else
2067             printf ("      ######    %s\n", l->text);
2068         }
2069       else
2070         {
2071           printf ("\t\t%s\n", l->text);
2072         }
2073           
2074       ++i;
2075     }
2076 }
2077
2078 static void
2079 print_block_superdetails (File *f)
2080 {
2081   DBusList *link;
2082   int i;
2083   
2084   link = _dbus_list_get_first_link (&f->functions);
2085   while (link != NULL)
2086     {
2087       Function *func = link->data;
2088
2089       printf ("=== %s():\n", func->name);
2090
2091       i = 0;
2092       while (i < func->n_blocks)
2093         {
2094           Block *b = &func->block_graph[i];
2095           DBusList *l;
2096           
2097           printf ("  %5d executed %d times%s\n", i,
2098                   (int) b->exec_count,
2099                   b->inside_dbus_build_tests ?
2100                   " [inside DBUS_BUILD_TESTS]" : "");
2101                   
2102           l = _dbus_list_get_first_link (&b->lines);
2103           while (l != NULL)
2104             {
2105               Line *line = l->data;
2106
2107               printf ("4%d\t%s\n", line->number, line->text);
2108
2109               l = _dbus_list_get_next_link (&b->lines, l);
2110             }
2111           
2112           ++i;
2113         }
2114       
2115       link = _dbus_list_get_next_link (&f->functions, link);
2116     }
2117 }
2118
2119 static void
2120 print_one_file (const DBusString *filename)
2121 {
2122   if (_dbus_string_ends_with_c_str (filename, ".bb"))
2123     {
2124       DBusString contents;
2125       DBusError error;
2126       
2127       if (!_dbus_string_init (&contents))
2128         die ("no memory\n");
2129       
2130       dbus_error_init (&error);
2131
2132       if (!_dbus_file_get_contents (&contents, filename,
2133                                     &error))
2134         {
2135           fprintf (stderr, "Could not open file: %s\n",
2136                    error.message);
2137           dbus_error_free (&error);
2138           exit (1);
2139         }
2140       
2141       dump_bb_file (&contents);
2142
2143       _dbus_string_free (&contents);
2144     }
2145   else if (_dbus_string_ends_with_c_str (filename, ".bbg"))
2146     {
2147       DBusString contents;
2148       DBusError error;
2149       
2150       if (!_dbus_string_init (&contents))
2151         die ("no memory\n");
2152       
2153       dbus_error_init (&error);
2154
2155       if (!_dbus_file_get_contents (&contents, filename,
2156                                     &error))
2157         {
2158           fprintf (stderr, "Could not open file: %s\n",
2159                    error.message);
2160           dbus_error_free (&error);
2161           exit (1);
2162         }
2163       
2164       dump_bbg_file (&contents);
2165
2166       _dbus_string_free (&contents);
2167     }
2168   else if (_dbus_string_ends_with_c_str (filename, ".da"))
2169     {
2170       DBusString contents;
2171       DBusError error;
2172       
2173       if (!_dbus_string_init (&contents))
2174         die ("no memory\n");
2175       
2176       dbus_error_init (&error);
2177
2178       if (!_dbus_file_get_contents (&contents, filename,
2179                                     &error))
2180         {
2181           fprintf (stderr, "Could not open file: %s\n",
2182                    error.message);
2183           dbus_error_free (&error);
2184           exit (1);
2185         }
2186       
2187       dump_da_file (&contents);
2188
2189       _dbus_string_free (&contents);
2190     }
2191   else if (_dbus_string_ends_with_c_str (filename, ".c"))
2192     {
2193       File *f;
2194       
2195       f = load_c_file (filename);
2196
2197       print_annotated_source (f);
2198     }
2199   else
2200     {
2201       fprintf (stderr, "Unknown file type %s\n",
2202                _dbus_string_get_const_data (filename));
2203       exit (1);
2204     }
2205 }
2206
2207 static void
2208 print_untested_functions (File *f)
2209 {
2210   DBusList *link;
2211   dbus_bool_t found;
2212
2213   found = FALSE;
2214   link = _dbus_list_get_first_link (&f->functions);
2215   while (link != NULL)
2216     {
2217       Function *func = link->data;
2218
2219       if (func->unused &&
2220           !func->inside_dbus_build_tests)
2221         found = TRUE;
2222       
2223       link = _dbus_list_get_next_link (&f->functions, link);
2224     }
2225
2226   if (!found)
2227     return;
2228   
2229   printf ("Untested functions in %s\n", f->name);
2230   printf ("=======\n");
2231   
2232   link = _dbus_list_get_first_link (&f->functions);
2233   while (link != NULL)
2234     {
2235       Function *func = link->data;
2236
2237       if (func->unused &&
2238           !func->inside_dbus_build_tests)
2239         printf ("  %s\n", func->name);
2240       
2241       link = _dbus_list_get_next_link (&f->functions, link);
2242     }
2243
2244   printf ("\n");
2245 }
2246
2247 static void
2248 print_poorly_tested_functions (File  *f,
2249                                Stats *stats)
2250 {
2251   DBusList *link;
2252   dbus_bool_t found;
2253
2254 #define TEST_FRACTION(function) ((function)->n_nontest_blocks_executed / (double) (function)->n_nontest_blocks)
2255
2256 #define AVERAGE_COVERAGE ((stats)->n_blocks_executed / (double) (stats)->n_blocks)
2257   
2258 #define POORLY_TESTED(function) (!(function)->unused &&                 \
2259                                  (function)->n_nontest_blocks > 0 &&    \
2260                                  TEST_FRACTION (function) < AVERAGE_COVERAGE)
2261   
2262   found = FALSE;
2263   link = _dbus_list_get_first_link (&f->functions);
2264   while (link != NULL)
2265     {
2266       Function *func = link->data;
2267
2268       if (POORLY_TESTED (func))
2269         found = TRUE;
2270       
2271       link = _dbus_list_get_next_link (&f->functions, link);
2272     }
2273
2274   if (!found)
2275     return;
2276
2277   printf ("Below average functions in %s\n", f->name);
2278   printf ("=======\n");
2279   
2280   link = _dbus_list_get_first_link (&f->functions);
2281   while (link != NULL)
2282     {
2283       Function *func = link->data;
2284
2285       if (POORLY_TESTED (func))
2286         printf ("  %s (%d%%)\n", func->name,
2287                 (int) (TEST_FRACTION (func) * 100));
2288       
2289       link = _dbus_list_get_next_link (&f->functions, link);
2290     }
2291
2292   printf ("\n");
2293 }
2294
2295 static int
2296 func_cmp (const void *a,
2297           const void *b)
2298 {
2299   Function *af = *(Function**) a;
2300   Function *bf = *(Function**) b;
2301   int a_untested = af->n_nontest_blocks - af->n_nontest_blocks_executed;
2302   int b_untested = bf->n_nontest_blocks - bf->n_nontest_blocks_executed;
2303   
2304   /* Sort by number of untested blocks */
2305   return b_untested - a_untested;
2306 }
2307
2308 static void
2309 print_n_untested_blocks_by_function (File  *f,
2310                                      Stats *stats)
2311 {
2312   DBusList *link;
2313   Function **funcs;
2314   int n_found;
2315   int i;
2316   
2317   n_found = 0;
2318   link = _dbus_list_get_first_link (&f->functions);
2319   while (link != NULL)
2320     {
2321       Function *func = link->data;
2322
2323       if (func->n_nontest_blocks_executed <
2324           func->n_nontest_blocks)
2325         n_found += 1;
2326       
2327       link = _dbus_list_get_next_link (&f->functions, link);
2328     }
2329
2330   if (n_found == 0)
2331     return;
2332
2333   /* make an array so we can use qsort */
2334   
2335   funcs = dbus_new (Function*, n_found);
2336   if (funcs == NULL)
2337     return;
2338   
2339   i = 0;
2340   link = _dbus_list_get_first_link (&f->functions);
2341   while (link != NULL)
2342     {
2343       Function *func = link->data;
2344
2345       if (func->n_nontest_blocks_executed <
2346           func->n_nontest_blocks)
2347         {
2348           funcs[i] = func;
2349           ++i;
2350         }
2351
2352       link = _dbus_list_get_next_link (&f->functions, link);
2353     }
2354
2355   _dbus_assert (i == n_found);
2356   
2357   qsort (funcs, n_found, sizeof (Function*),
2358          func_cmp);
2359   
2360   printf ("Incomplete functions in %s\n", f->name);
2361   printf ("=======\n");
2362
2363   i = 0;
2364   while (i < n_found)
2365     {
2366       Function *func = funcs[i];
2367
2368       printf ("  %s (%d/%d untested blocks)\n",
2369               func->name,
2370               func->n_nontest_blocks - func->n_nontest_blocks_executed,
2371               func->n_nontest_blocks);
2372       
2373       ++i;
2374     }
2375
2376   dbus_free (funcs);
2377
2378   printf ("\n");
2379 }
2380
2381 static void
2382 print_stats (Stats      *stats,
2383              const char *of_what)
2384 {
2385   int completely;
2386   
2387   printf ("Summary (%s)\n", of_what);
2388   printf ("=======\n");
2389   printf ("  %g%% blocks executed (%d of %d)\n",
2390           (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0,
2391           stats->n_blocks_executed,
2392           stats->n_blocks);
2393
2394   printf ("     (ignored %d blocks of test-only/debug-only code)\n",
2395           stats->n_blocks_inside_dbus_build_tests);
2396       
2397   printf ("  %g%% functions executed (%d of %d)\n",
2398           (stats->n_functions_executed / (double) stats->n_functions) * 100.0,
2399           stats->n_functions_executed,
2400           stats->n_functions);
2401
2402   completely = stats->n_functions_executed - stats->n_functions_partial;
2403   printf ("  %g%% functions completely executed (%d of %d)\n",
2404           (completely / (double) stats->n_functions) * 100.0,
2405           completely,
2406           stats->n_functions);
2407
2408   printf ("     (ignored %d functions of test-only/debug-only code)\n",
2409           stats->n_functions_inside_dbus_build_tests);
2410       
2411   printf ("  %g%% lines executed (%d of %d)\n",
2412           (stats->n_lines_executed / (double) stats->n_lines) * 100.0,
2413           stats->n_lines_executed,
2414           stats->n_lines);
2415
2416   completely = stats->n_lines_executed - stats->n_lines_partial;
2417   printf ("  %g%% lines completely executed (%d of %d)\n",
2418           (completely / (double) stats->n_lines) * 100.0,
2419           completely,
2420           stats->n_lines);
2421
2422   printf ("     (ignored %d lines of test-only/debug-only code)\n",
2423           stats->n_lines_inside_dbus_build_tests);
2424
2425   printf ("\n");
2426 }
2427
2428 typedef enum
2429 {
2430   MODE_PRINT,
2431   MODE_REPORT,
2432   MODE_BLOCKS,
2433   MODE_GCOV
2434 } Mode;
2435
2436 int
2437 main (int argc, char **argv)
2438 {
2439   DBusString filename;
2440   int i;
2441   Mode m;
2442   
2443   if (argc < 2)
2444     {
2445       fprintf (stderr, "Must specify files on command line\n");
2446       return 1;
2447     }
2448
2449   m = MODE_PRINT;
2450   i = 1;
2451
2452   if (strcmp (argv[i], "--report") == 0)
2453     {
2454       m = MODE_REPORT;
2455       ++i;
2456     }
2457   else if (strcmp (argv[i], "--blocks") == 0)
2458     {
2459       m = MODE_BLOCKS;
2460       ++i;
2461     }
2462   else if (strcmp (argv[i], "--gcov") == 0)
2463     {
2464       m = MODE_GCOV;
2465       ++i;
2466     }
2467
2468   
2469   if (i == argc)
2470     {
2471       fprintf (stderr, "Must specify files on command line\n");
2472       return 1;
2473     }
2474
2475   if (m == MODE_PRINT)
2476     {
2477       while (i < argc)
2478         {
2479           _dbus_string_init_const (&filename, argv[i]);
2480           
2481           print_one_file (&filename);
2482           
2483           ++i;
2484         }
2485     }
2486   else if (m == MODE_BLOCKS || m == MODE_GCOV)
2487     {
2488       while (i < argc)
2489         {
2490           File *f;
2491           
2492           _dbus_string_init_const (&filename, argv[i]);
2493       
2494           f = load_c_file (&filename);
2495
2496           if (m == MODE_BLOCKS)
2497             print_block_superdetails (f);
2498           else if (m == MODE_GCOV)
2499             print_annotated_source_gcov_format (f);
2500           
2501           ++i;
2502         }
2503     }
2504   else if (m == MODE_REPORT)
2505     {
2506       Stats stats = { 0, };
2507       DBusList *files;
2508       DBusList *link;
2509       DBusHashTable *stats_by_dir;
2510       DBusHashIter iter;
2511       
2512       files = NULL;
2513       while (i < argc)
2514         {
2515           _dbus_string_init_const (&filename, argv[i]);
2516
2517           if (_dbus_string_ends_with_c_str (&filename, ".c"))
2518             {
2519               File *f;
2520               
2521               f = load_c_file (&filename);
2522               
2523               if (!_dbus_list_append (&files, f))
2524                 die ("no memory\n");
2525             }
2526           else
2527             {
2528               fprintf (stderr, "Unknown file type %s\n",
2529                        _dbus_string_get_const_data (&filename));
2530               exit (1);
2531             }
2532           
2533           ++i;
2534         }
2535
2536       link = _dbus_list_get_first_link (&files);
2537       while (link != NULL)
2538         {
2539           File *f = link->data;
2540
2541           merge_stats_for_file (&stats, f);
2542           
2543           link = _dbus_list_get_next_link (&files, link);
2544         }
2545
2546       print_stats (&stats, "all files");
2547
2548       stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING,
2549                                            dbus_free, dbus_free);
2550       
2551       link = _dbus_list_get_first_link (&files);
2552       while (link != NULL)
2553         {
2554           File *f = link->data;
2555           DBusString dirname;
2556           char *dirname_c;
2557           Stats *dir_stats;
2558           
2559           _dbus_string_init_const (&filename, f->name);
2560             
2561           if (!_dbus_string_init (&dirname))
2562             die ("no memory\n");
2563
2564           if (!_dbus_string_get_dirname (&filename, &dirname) ||
2565               !_dbus_string_copy_data (&dirname, &dirname_c))
2566             die ("no memory\n");
2567
2568           dir_stats = _dbus_hash_table_lookup_string (stats_by_dir,
2569                                                       dirname_c);
2570
2571           if (dir_stats == NULL)
2572             {
2573               dir_stats = dbus_new0 (Stats, 1);
2574               if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c,
2575                                                    dir_stats))
2576                 die ("no memory\n");
2577             }
2578           else
2579             dbus_free (dirname_c);
2580           
2581           merge_stats_for_file (dir_stats, f);
2582           
2583           link = _dbus_list_get_next_link (&files, link);
2584         }
2585
2586       _dbus_hash_iter_init (stats_by_dir, &iter);
2587       while (_dbus_hash_iter_next (&iter))
2588         {
2589           const char *dirname = _dbus_hash_iter_get_string_key (&iter);
2590           Stats *dir_stats = _dbus_hash_iter_get_value (&iter);
2591
2592           print_stats (dir_stats, dirname);
2593         }
2594
2595       _dbus_hash_table_unref (stats_by_dir);
2596
2597       link = _dbus_list_get_first_link (&files);
2598       while (link != NULL)
2599         {
2600           File *f = link->data;
2601
2602           print_untested_functions (f);
2603           
2604           link = _dbus_list_get_next_link (&files, link);
2605         }
2606
2607       link = _dbus_list_get_first_link (&files);
2608       while (link != NULL)
2609         {
2610           File *f = link->data;
2611
2612           print_poorly_tested_functions (f, &stats);
2613           
2614           link = _dbus_list_get_next_link (&files, link);
2615         }
2616
2617       link = _dbus_list_get_first_link (&files);
2618       while (link != NULL)
2619         {
2620           File *f = link->data;
2621           
2622           print_n_untested_blocks_by_function (f, &stats);
2623           
2624           link = _dbus_list_get_next_link (&files, link);
2625         }
2626     }
2627   
2628   return 0;
2629 }