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