* dbus/dbus-auth-script.c (_dbus_auth_script_run): added UNIX_ONLY and WIN_ONLY comma...
[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 gcno_filename;
1278   DBusString gcda_filename;
1279   DBusString contents;
1280   DBusString *name;
1281   DBusError error;
1282
1283   /* With latest gcc it's .gcno instead of .bbg and
1284    * gcda instead of .da
1285    */
1286   
1287   dbus_error_init (&error);
1288   
1289   if (!_dbus_string_init (&bbg_filename) ||
1290       !_dbus_string_init (&da_filename) ||
1291       !_dbus_string_init (&gcno_filename) ||
1292       !_dbus_string_init (&gcda_filename) ||
1293       !_dbus_string_copy (filename, 0, &bbg_filename, 0) ||
1294       !_dbus_string_copy (filename, 0, &da_filename, 0) ||
1295       !_dbus_string_copy (filename, 0, &gcno_filename, 0) ||
1296       !_dbus_string_copy (filename, 0, &gcda_filename, 0) ||
1297       !_dbus_string_init (&contents))
1298     die ("no memory\n");
1299
1300   _dbus_string_shorten (&bbg_filename, 2);
1301   _dbus_string_shorten (&da_filename, 2);
1302
1303   if (!_dbus_string_append (&bbg_filename, ".bbg") ||
1304       !_dbus_string_append (&da_filename, ".da") ||
1305       !_dbus_string_append (&bbg_filename, ".gcno") ||
1306       !_dbus_string_append (&bbg_filename, ".gcda"))
1307     die ("no memory\n");
1308
1309   if (_dbus_file_exists (_dbus_string_get_const_data (&gcno_filename)))
1310     name = &gcno_filename;
1311   else
1312     name = &bbg_filename;
1313   
1314   if (!_dbus_file_get_contents (&contents, name,
1315                                 &error))
1316     {
1317       fprintf (stderr, "Could not open file: %s\n",
1318                error.message);
1319       exit (1);
1320     }
1321
1322   get_functions_from_bbg (&contents, functions);
1323
1324   _dbus_string_set_length (&contents, 0);
1325
1326   if (_dbus_file_exists (_dbus_string_get_const_data (&gcda_filename)))
1327     name = &gcda_filename;
1328   else
1329     name = &da_filename;
1330   
1331   if (!_dbus_file_get_contents (&contents, name,
1332                                 &error))
1333     {
1334       /* Try .libs/file.da */
1335       int slash;
1336
1337       if (_dbus_string_find_byte_backward (name,
1338                                            _dbus_string_get_length (name),
1339                                            '/',
1340                                            &slash))
1341         {
1342           DBusString libs;
1343           _dbus_string_init_const (&libs, "/.libs");
1344
1345           if (!_dbus_string_copy (&libs, 0, name, slash))
1346             die ("no memory");
1347
1348           dbus_error_free (&error);
1349           if (!_dbus_file_get_contents (&contents, name,
1350                                         &error))
1351             {
1352               fprintf (stderr, "Could not open file: %s\n",
1353                        error.message);
1354               exit (1);
1355             }
1356         }
1357       else
1358         {
1359           fprintf (stderr, "Could not open file: %s\n",
1360                    error.message);
1361           exit (1);
1362         }
1363     }
1364   
1365   add_counts_from_da (&contents, functions);
1366   
1367   solve_graphs (functions);
1368
1369   _dbus_string_free (&contents);
1370   _dbus_string_free (&da_filename);
1371   _dbus_string_free (&bbg_filename);
1372 }
1373
1374 static void
1375 get_lines_from_bb_file (const DBusString *contents,
1376                         File             *fl)
1377 {
1378   int i;
1379   long val;
1380   int n_functions;
1381   dbus_bool_t in_our_file;
1382   DBusList *link;
1383   Function *func;
1384   int block;
1385
1386 #if 0
1387   printf ("Getting line numbers for blocks from .bb file\n");
1388 #endif
1389   
1390   /* There's this "filename" field in the .bb file which
1391    * mysteriously comes *after* the first function in the
1392    * file in the .bb file; and every .bb file seems to
1393    * have only one filename. I don't understand
1394    * what's going on here, so just set in_our_file = TRUE
1395    * at the start categorically.
1396    */
1397   
1398   block = 0;
1399   func = NULL;
1400   in_our_file = TRUE;
1401   link = _dbus_list_get_first_link (&fl->functions);
1402   n_functions = 0;
1403   i = 0;
1404   while (string_get_int (contents, i, &val))
1405     {
1406       i += 4;
1407       
1408       switch (val)
1409         {
1410         case BB_FILENAME:
1411           {
1412             DBusString f;
1413
1414             if (!_dbus_string_init (&f))
1415               die ("no memory\n");
1416
1417             if (string_get_string (contents, i,
1418                                    BB_FILENAME,
1419                                    &f, &i))
1420               {
1421                 /* fl->name is a full path and the filename in .bb is
1422                  * not.
1423                  */
1424                 DBusString tmp_str;
1425
1426                 _dbus_string_init_const (&tmp_str, fl->name);
1427                 
1428                 if (_dbus_string_ends_with_c_str (&tmp_str,
1429                                                   _dbus_string_get_const_data (&f)))
1430                   in_our_file = TRUE;
1431                 else
1432                   in_our_file = FALSE;
1433                 
1434 #if 0
1435                 fprintf (stderr,
1436                          "File %s in .bb, looking for %s, in_our_file = %d\n",
1437                          _dbus_string_get_const_data (&f),
1438                          fl->name,
1439                          in_our_file);
1440 #endif
1441               }
1442             _dbus_string_free (&f);
1443           }
1444           break;
1445         case BB_FUNCTION:
1446           {
1447             DBusString f;
1448             if (!_dbus_string_init (&f))
1449               die ("no memory\n");
1450
1451             if (string_get_string (contents, i,
1452                                    BB_FUNCTION,
1453                                    &f, &i))
1454               {
1455 #if 0
1456                 fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f));
1457 #endif
1458
1459                 block = 0;
1460                 
1461                 if (in_our_file)
1462                   {
1463                     if (link == NULL)
1464                       {
1465                         fprintf (stderr, "No function object for function %s\n",
1466                                  _dbus_string_get_const_data (&f));
1467                       }
1468                     else
1469                       {
1470                         func = link->data;
1471                         link = _dbus_list_get_next_link (&fl->functions, link);
1472
1473                         if (func->name == NULL)
1474                           {
1475                             if (!_dbus_string_copy_data (&f, &func->name))
1476                               die ("no memory\n");
1477                           }
1478                         else
1479                           {
1480                             if (!_dbus_string_equal_c_str (&f, func->name))
1481                               {
1482                                 fprintf (stderr, "got function name \"%s\" (%d) from .bbg file, but \"%s\" (%d) from .bb file\n",
1483                                          func->name, strlen (func->name),
1484                                          _dbus_string_get_const_data (&f),
1485                                          _dbus_string_get_length (&f));
1486
1487                               }
1488                           }
1489                       }
1490                   }
1491               }
1492             _dbus_string_free (&f);
1493
1494             n_functions += 1;
1495           }
1496           break;
1497         case BB_ENDOFLIST:
1498           block += 1;
1499           break;
1500         default:
1501 #if 0
1502           fprintf (stderr, "Line %ld\n", val);
1503 #endif
1504
1505           if (val >= fl->n_lines)
1506             {
1507               fprintf (stderr, "Line %ld but file only has %d lines\n",
1508                        val, fl->n_lines);
1509             }
1510           else if (func != NULL)
1511             {
1512               val -= 1; /* To convert the 1-based line number to 0-based */
1513               _dbus_assert (val >= 0);
1514               
1515               if (block < func->n_blocks)
1516                 {
1517                   if (!_dbus_list_append (&func->block_graph[block].lines,
1518                                           &fl->lines[val]))
1519                     die ("no memory\n");
1520                   
1521                   
1522                   if (!_dbus_list_append (&fl->lines[val].blocks,
1523                                           &func->block_graph[block]))
1524                     die ("no memory\n");
1525                 }
1526               else
1527                 {
1528                   fprintf (stderr, "Line number for block %d but function only has %d blocks\n",
1529                            block, func->n_blocks);
1530                 }
1531             }
1532           else
1533             {
1534               fprintf (stderr, "Line %ld given outside of any function\n",
1535                        val);
1536             }
1537           
1538           break;
1539         }
1540     }
1541
1542 #if 0
1543   printf ("%d functions in file\n", n_functions);
1544 #endif
1545 }
1546
1547
1548 static void
1549 load_block_line_associations (const DBusString *filename,
1550                               File             *f)
1551 {
1552   DBusString bb_filename;
1553   DBusString contents;
1554   DBusError error;
1555
1556   dbus_error_init (&error);
1557   
1558   if (!_dbus_string_init (&bb_filename) ||
1559       !_dbus_string_copy (filename, 0, &bb_filename, 0) ||
1560       !_dbus_string_init (&contents))
1561     die ("no memory\n");
1562
1563   _dbus_string_shorten (&bb_filename, 2);
1564
1565   if (!_dbus_string_append (&bb_filename, ".bb"))
1566     die ("no memory\n");
1567       
1568   if (!_dbus_file_get_contents (&contents, &bb_filename,
1569                                 &error))
1570     {
1571       fprintf (stderr, "Could not open file: %s\n",
1572                error.message);
1573       exit (1);
1574     }
1575   
1576   get_lines_from_bb_file (&contents, f);
1577
1578   _dbus_string_free (&contents);
1579   _dbus_string_free (&bb_filename);
1580 }
1581
1582 static int
1583 count_lines_in_string (const DBusString *str)
1584 {
1585   int n_lines;
1586   const char *p;
1587   const char *prev;
1588   const char *end;
1589   const char *last_line_end;
1590
1591 #if 0
1592   printf ("Counting lines in source file\n");
1593 #endif
1594   
1595   n_lines = 0;  
1596   prev = NULL;
1597   p = _dbus_string_get_const_data (str);
1598   end = p + _dbus_string_get_length (str);
1599   last_line_end = p;
1600   while (p != end)
1601     {
1602       /* too lazy to handle \r\n as one linebreak */
1603       if (*p == '\n' || *p == '\r')
1604         {
1605           ++n_lines;
1606           last_line_end = p + 1;
1607         }
1608
1609       prev = p;
1610       ++p;
1611     }
1612
1613   if (last_line_end != p)
1614     ++n_lines;
1615   
1616   return n_lines;
1617 }
1618
1619 static void
1620 fill_line_content (const DBusString *str,
1621                    Line             *lines)
1622 {
1623   int n_lines;
1624   const char *p;
1625   const char *prev;
1626   const char *end;
1627   const char *last_line_end;
1628
1629 #if 0
1630   printf ("Saving contents of each line in source file\n");
1631 #endif
1632   
1633   n_lines = 0;
1634   prev = NULL;
1635   p = _dbus_string_get_const_data (str);
1636   end = p + _dbus_string_get_length (str);
1637   last_line_end = p;
1638   while (p != end)
1639     {
1640       if (*p == '\n' || *p == '\r')
1641         {
1642           lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1);
1643           if (lines[n_lines].text == NULL)
1644             die ("no memory\n");
1645
1646           memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1647           lines[n_lines].number = n_lines + 1;
1648           
1649           ++n_lines;
1650
1651           last_line_end = p + 1;
1652         }
1653
1654       prev = p;
1655       ++p;
1656     }
1657
1658   if (p != last_line_end)
1659     {
1660       memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1661       ++n_lines;
1662     }
1663 }
1664
1665 static void
1666 mark_inside_dbus_build_tests (File  *f)
1667 {
1668   int i;
1669   DBusList *link;
1670   int inside_depth;
1671
1672   inside_depth = 0;
1673   i = 0;
1674   while (i < f->n_lines)
1675     {
1676       Line *l = &f->lines[i];
1677       dbus_bool_t is_verbose;
1678
1679       is_verbose = strstr (l->text, "_dbus_verbose") != NULL;
1680
1681       if (inside_depth == 0)
1682         {
1683           const char *a, *b;
1684           
1685           a = strstr (l->text, "#if");
1686           b = strstr (l->text, "DBUS_BUILD_TESTS");
1687           if (a && b && (a < b))
1688             inside_depth += 1;
1689         }
1690       else
1691         {
1692           if (strstr (l->text, "#if") != NULL)
1693             inside_depth += 1;
1694           else if (strstr (l->text, "#endif") != NULL)
1695             inside_depth -= 1;
1696         }
1697
1698       if (inside_depth > 0 || is_verbose)
1699         {
1700           /* Mark the line and its blocks */
1701           DBusList *blink;
1702
1703           l->inside_dbus_build_tests = TRUE;
1704           
1705           blink = _dbus_list_get_first_link (&l->blocks);
1706           while (blink != NULL)
1707             {
1708               Block *b = blink->data;
1709
1710               b->inside_dbus_build_tests = TRUE;
1711               
1712               blink = _dbus_list_get_next_link (&l->blocks, blink);
1713             }
1714         }
1715       
1716       ++i;
1717     }
1718
1719   /* Now mark functions where for all blocks that are associated
1720    * with a source line, the block is inside_dbus_build_tests.
1721    */
1722   link = _dbus_list_get_first_link (&f->functions);
1723   while (link != NULL)
1724     {
1725       Function *func = link->data;
1726
1727       /* The issue is that some blocks aren't associated with a source line.
1728        * Assume they are inside/outside tests according to the source
1729        * line of the preceding block. For the first block, make it
1730        * match the first following block with a line associated.
1731        */
1732       if (func->block_graph[0].lines == NULL)
1733         {
1734           /* find first following line */
1735           i = 1;
1736           while (i < func->n_blocks)
1737             {
1738               if (func->block_graph[i].lines != NULL)
1739                 {
1740                   func->block_graph[0].inside_dbus_build_tests =
1741                     func->block_graph[i].inside_dbus_build_tests;
1742                   break;
1743                 }
1744               
1745               ++i;
1746             }
1747         }
1748
1749       /* Now mark all blocks but the first */
1750       i = 1;
1751       while (i < func->n_blocks)
1752         {
1753           if (func->block_graph[i].lines == NULL)
1754             {
1755               func->block_graph[i].inside_dbus_build_tests =
1756                 func->block_graph[i-1].inside_dbus_build_tests;
1757             }
1758           
1759           ++i;
1760         }
1761       
1762       i = 0;
1763       while (i < func->n_blocks)
1764         {
1765           /* Break as soon as any block is not a test block */
1766           if (func->block_graph[i].lines != NULL &&
1767               !func->block_graph[i].inside_dbus_build_tests)
1768             break;
1769           
1770           ++i;
1771         }
1772
1773       if (i == func->n_blocks)
1774         func->inside_dbus_build_tests = TRUE;
1775       
1776       link = _dbus_list_get_next_link (&f->functions, link);
1777     } 
1778 }
1779
1780 static void
1781 mark_coverage (File  *f)
1782 {
1783   int i;
1784   DBusList *link;
1785   
1786   i = 0;
1787   while (i < f->n_lines)
1788     {
1789       Line *l = &f->lines[i];
1790       DBusList *blink;
1791       int n_blocks;
1792       int n_blocks_executed;
1793
1794       n_blocks = 0;
1795       n_blocks_executed = 0;
1796       blink = _dbus_list_get_first_link (&l->blocks);
1797       while (blink != NULL)
1798         {
1799           Block *b = blink->data;
1800           
1801           if (b->exec_count > 0)
1802             n_blocks_executed += 1;
1803
1804           n_blocks += 1;
1805           
1806           blink = _dbus_list_get_next_link (&l->blocks, blink);
1807         }
1808
1809       if (n_blocks_executed > 0 &&
1810           n_blocks_executed < n_blocks)
1811         l->partial = TRUE;
1812
1813       ++i;
1814     }
1815
1816   link = _dbus_list_get_first_link (&f->functions);
1817   while (link != NULL)
1818     {
1819       Function *func = link->data;
1820       int i;
1821       int n_test_blocks;
1822       int n_test_blocks_executed;
1823       int n_nontest_blocks;
1824       int n_nontest_blocks_executed;
1825       
1826       n_test_blocks = 0;
1827       n_test_blocks_executed = 0;
1828       n_nontest_blocks = 0;
1829       n_nontest_blocks_executed = 0;      
1830
1831       i = 0;
1832       while (i < func->n_blocks)
1833         {
1834           if (!func->block_graph[i].inside_dbus_build_tests)
1835             {
1836               n_nontest_blocks += 1;
1837
1838               if (func->block_graph[i].exec_count > 0)
1839                 n_nontest_blocks_executed += 1;
1840             }
1841           else
1842             {
1843               n_test_blocks += 1;
1844
1845               if (func->block_graph[i].exec_count > 0)
1846                 n_test_blocks_executed += 1;
1847             }
1848
1849           ++i;
1850         }
1851       
1852       if (n_nontest_blocks_executed > 0 &&
1853           n_nontest_blocks_executed < n_nontest_blocks)
1854         func->partial = TRUE;
1855
1856       if (n_nontest_blocks_executed == 0 &&
1857           n_nontest_blocks > 0)
1858         func->unused = TRUE;
1859       
1860       func->n_test_blocks = n_test_blocks;
1861       func->n_test_blocks_executed = n_test_blocks_executed;
1862       func->n_nontest_blocks = n_nontest_blocks;
1863       func->n_nontest_blocks_executed = n_nontest_blocks_executed;
1864       
1865       link = _dbus_list_get_next_link (&f->functions, link);
1866     }
1867 }
1868
1869 static File*
1870 load_c_file (const DBusString *filename)
1871 {
1872   DBusString contents;
1873   DBusError error;
1874   File *f;
1875   
1876   f = dbus_new0 (File, 1);
1877   if (f == NULL)
1878     die ("no memory\n");
1879
1880   if (!_dbus_string_copy_data (filename, &f->name))
1881     die ("no memory\n");
1882   
1883   if (!_dbus_string_init (&contents))
1884     die ("no memory\n");
1885       
1886   dbus_error_init (&error);
1887
1888   if (!_dbus_file_get_contents (&contents, filename,
1889                                 &error))
1890     {
1891       fprintf (stderr, "Could not open file: %s\n",
1892                error.message);
1893       dbus_error_free (&error);
1894       exit (1);
1895     }
1896       
1897   load_functions_for_c_file (filename, &f->functions);
1898
1899   f->n_lines = count_lines_in_string (&contents);
1900   f->lines = dbus_new0 (Line, f->n_lines);
1901   if (f->lines == NULL)
1902     die ("no memory\n");
1903
1904   fill_line_content (&contents, f->lines);
1905   
1906   _dbus_string_free (&contents);
1907
1908   load_block_line_associations (filename, f);
1909
1910   mark_inside_dbus_build_tests (f);
1911   mark_coverage (f);
1912   
1913   return f;
1914 }
1915
1916 typedef struct Stats Stats;
1917
1918 struct Stats
1919 {
1920   int n_blocks;
1921   int n_blocks_executed;
1922   int n_blocks_inside_dbus_build_tests;
1923   
1924   int n_lines; /* lines that have blocks on them */
1925   int n_lines_executed;
1926   int n_lines_partial;
1927   int n_lines_inside_dbus_build_tests;
1928   
1929   int n_functions;
1930   int n_functions_executed;
1931   int n_functions_partial;
1932   int n_functions_inside_dbus_build_tests;
1933 };
1934
1935 static dbus_bool_t
1936 line_was_executed (Line *l)
1937 {
1938   DBusList *link;
1939
1940   link = _dbus_list_get_first_link (&l->blocks);
1941   while (link != NULL)
1942     {
1943       Block *b = link->data;
1944
1945       if (b->exec_count > 0)
1946         return TRUE;
1947       
1948       link = _dbus_list_get_next_link (&l->blocks, link);
1949     }
1950
1951   return FALSE;
1952 }
1953
1954
1955 static int
1956 line_exec_count (Line *l)
1957 {
1958   DBusList *link;
1959   dbus_int64_t total;
1960
1961   total = 0;
1962   link = _dbus_list_get_first_link (&l->blocks);
1963   while (link != NULL)
1964     {
1965       Block *b = link->data;
1966
1967       total += b->exec_count;
1968       
1969       link = _dbus_list_get_next_link (&l->blocks, link);
1970     }
1971
1972   return total;
1973 }
1974
1975 static void
1976 merge_stats_for_file (Stats *stats,
1977                       File  *f)
1978 {
1979   int i;
1980   DBusList *link;
1981   
1982   for (i = 0; i < f->n_lines; ++i)
1983     {
1984       Line *l = &f->lines[i];
1985       
1986       if (l->inside_dbus_build_tests)
1987         {
1988           stats->n_lines_inside_dbus_build_tests += 1;
1989           continue;
1990         }
1991       
1992       if (line_was_executed (l))
1993         stats->n_lines_executed += 1;
1994
1995       if (l->blocks != NULL)
1996         stats->n_lines += 1;
1997
1998       if (l->partial)
1999         stats->n_lines_partial += 1;
2000     }
2001
2002   link = _dbus_list_get_first_link (&f->functions);
2003   while (link != NULL)
2004     {
2005       Function *func = link->data;
2006
2007       if (func->inside_dbus_build_tests)
2008         stats->n_functions_inside_dbus_build_tests += 1;
2009       else
2010         {
2011           stats->n_functions += 1;
2012
2013           if (!func->unused)
2014             stats->n_functions_executed += 1;
2015           
2016           if (func->partial)
2017             stats->n_functions_partial += 1;
2018         }
2019
2020       stats->n_blocks_inside_dbus_build_tests +=
2021         func->n_test_blocks;
2022       
2023       stats->n_blocks_executed +=
2024         func->n_nontest_blocks_executed;
2025       
2026       stats->n_blocks +=
2027         func->n_nontest_blocks;
2028
2029       link = _dbus_list_get_next_link (&f->functions, link);
2030     }
2031 }
2032
2033 /* The output of this matches gcov exactly ("diff" shows no difference) */
2034 static void
2035 print_annotated_source_gcov_format (File *f)
2036 {
2037   int i;
2038   
2039   i = 0;
2040   while (i < f->n_lines)
2041     {
2042       Line *l = &f->lines[i];
2043
2044       if (l->blocks != NULL)
2045         {
2046           int exec_count;
2047           
2048           exec_count = line_exec_count (l);
2049           
2050           if (exec_count > 0)
2051             printf ("%12d    %s\n",
2052                     exec_count, l->text);
2053           else
2054             printf ("      ######    %s\n", l->text);
2055         }
2056       else
2057         {
2058           printf ("\t\t%s\n", l->text);
2059         }
2060           
2061       ++i;
2062     }
2063 }
2064
2065 static void
2066 print_annotated_source (File *f)
2067 {
2068   int i;
2069   
2070   i = 0;
2071   while (i < f->n_lines)
2072     {
2073       Line *l = &f->lines[i];
2074
2075       if (l->inside_dbus_build_tests)
2076         printf ("*");
2077       else
2078         printf (" ");
2079       
2080       if (l->blocks != NULL)
2081         {
2082           int exec_count;
2083           
2084           exec_count = line_exec_count (l);
2085           
2086           if (exec_count > 0)
2087             printf ("%12d    %s\n",
2088                     exec_count, l->text);
2089           else
2090             printf ("      ######    %s\n", l->text);
2091         }
2092       else
2093         {
2094           printf ("\t\t%s\n", l->text);
2095         }
2096           
2097       ++i;
2098     }
2099 }
2100
2101 static void
2102 print_block_superdetails (File *f)
2103 {
2104   DBusList *link;
2105   int i;
2106   
2107   link = _dbus_list_get_first_link (&f->functions);
2108   while (link != NULL)
2109     {
2110       Function *func = link->data;
2111
2112       printf ("=== %s():\n", func->name);
2113
2114       i = 0;
2115       while (i < func->n_blocks)
2116         {
2117           Block *b = &func->block_graph[i];
2118           DBusList *l;
2119           
2120           printf ("  %5d executed %d times%s\n", i,
2121                   (int) b->exec_count,
2122                   b->inside_dbus_build_tests ?
2123                   " [inside DBUS_BUILD_TESTS]" : "");
2124                   
2125           l = _dbus_list_get_first_link (&b->lines);
2126           while (l != NULL)
2127             {
2128               Line *line = l->data;
2129
2130               printf ("4%d\t%s\n", line->number, line->text);
2131
2132               l = _dbus_list_get_next_link (&b->lines, l);
2133             }
2134           
2135           ++i;
2136         }
2137       
2138       link = _dbus_list_get_next_link (&f->functions, link);
2139     }
2140 }
2141
2142 static void
2143 print_one_file (const DBusString *filename)
2144 {
2145   if (_dbus_string_ends_with_c_str (filename, ".bb"))
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_bb_file (&contents);
2165
2166       _dbus_string_free (&contents);
2167     }
2168   else if (_dbus_string_ends_with_c_str (filename, ".bbg"))
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_bbg_file (&contents);
2188
2189       _dbus_string_free (&contents);
2190     }
2191   else if (_dbus_string_ends_with_c_str (filename, ".da"))
2192     {
2193       DBusString contents;
2194       DBusError error;
2195       
2196       if (!_dbus_string_init (&contents))
2197         die ("no memory\n");
2198       
2199       dbus_error_init (&error);
2200
2201       if (!_dbus_file_get_contents (&contents, filename,
2202                                     &error))
2203         {
2204           fprintf (stderr, "Could not open file: %s\n",
2205                    error.message);
2206           dbus_error_free (&error);
2207           exit (1);
2208         }
2209       
2210       dump_da_file (&contents);
2211
2212       _dbus_string_free (&contents);
2213     }
2214   else if (_dbus_string_ends_with_c_str (filename, ".c"))
2215     {
2216       File *f;
2217       
2218       f = load_c_file (filename);
2219
2220       print_annotated_source (f);
2221     }
2222   else
2223     {
2224       fprintf (stderr, "Unknown file type %s\n",
2225                _dbus_string_get_const_data (filename));
2226       exit (1);
2227     }
2228 }
2229
2230 static void
2231 print_untested_functions (File *f)
2232 {
2233   DBusList *link;
2234   dbus_bool_t found;
2235
2236   found = FALSE;
2237   link = _dbus_list_get_first_link (&f->functions);
2238   while (link != NULL)
2239     {
2240       Function *func = link->data;
2241
2242       if (func->unused &&
2243           !func->inside_dbus_build_tests)
2244         found = TRUE;
2245       
2246       link = _dbus_list_get_next_link (&f->functions, link);
2247     }
2248
2249   if (!found)
2250     return;
2251   
2252   printf ("Untested functions in %s\n", f->name);
2253   printf ("=======\n");
2254   
2255   link = _dbus_list_get_first_link (&f->functions);
2256   while (link != NULL)
2257     {
2258       Function *func = link->data;
2259
2260       if (func->unused &&
2261           !func->inside_dbus_build_tests)
2262         printf ("  %s\n", func->name);
2263       
2264       link = _dbus_list_get_next_link (&f->functions, link);
2265     }
2266
2267   printf ("\n");
2268 }
2269
2270 static void
2271 print_poorly_tested_functions (File  *f,
2272                                Stats *stats)
2273 {
2274   DBusList *link;
2275   dbus_bool_t found;
2276
2277 #define TEST_FRACTION(function) ((function)->n_nontest_blocks_executed / (double) (function)->n_nontest_blocks)
2278
2279 #define AVERAGE_COVERAGE ((stats)->n_blocks_executed / (double) (stats)->n_blocks)
2280   
2281 #define POORLY_TESTED(function) (!(function)->unused &&                 \
2282                                  (function)->n_nontest_blocks > 0 &&    \
2283                                  TEST_FRACTION (function) < AVERAGE_COVERAGE)
2284   
2285   found = FALSE;
2286   link = _dbus_list_get_first_link (&f->functions);
2287   while (link != NULL)
2288     {
2289       Function *func = link->data;
2290
2291       if (POORLY_TESTED (func))
2292         found = TRUE;
2293       
2294       link = _dbus_list_get_next_link (&f->functions, link);
2295     }
2296
2297   if (!found)
2298     return;
2299
2300   printf ("Below average functions in %s\n", f->name);
2301   printf ("=======\n");
2302   
2303   link = _dbus_list_get_first_link (&f->functions);
2304   while (link != NULL)
2305     {
2306       Function *func = link->data;
2307
2308       if (POORLY_TESTED (func))
2309         printf ("  %s (%d%%)\n", func->name,
2310                 (int) (TEST_FRACTION (func) * 100));
2311       
2312       link = _dbus_list_get_next_link (&f->functions, link);
2313     }
2314
2315   printf ("\n");
2316 }
2317
2318 static int
2319 func_cmp (const void *a,
2320           const void *b)
2321 {
2322   Function *af = *(Function**) a;
2323   Function *bf = *(Function**) b;
2324   int a_untested = af->n_nontest_blocks - af->n_nontest_blocks_executed;
2325   int b_untested = bf->n_nontest_blocks - bf->n_nontest_blocks_executed;
2326   
2327   /* Sort by number of untested blocks */
2328   return b_untested - a_untested;
2329 }
2330
2331 static void
2332 print_n_untested_blocks_by_function (File  *f,
2333                                      Stats *stats)
2334 {
2335   DBusList *link;
2336   Function **funcs;
2337   int n_found;
2338   int i;
2339   
2340   n_found = 0;
2341   link = _dbus_list_get_first_link (&f->functions);
2342   while (link != NULL)
2343     {
2344       Function *func = link->data;
2345
2346       if (func->n_nontest_blocks_executed <
2347           func->n_nontest_blocks)
2348         n_found += 1;
2349       
2350       link = _dbus_list_get_next_link (&f->functions, link);
2351     }
2352
2353   if (n_found == 0)
2354     return;
2355
2356   /* make an array so we can use qsort */
2357   
2358   funcs = dbus_new (Function*, n_found);
2359   if (funcs == NULL)
2360     return;
2361   
2362   i = 0;
2363   link = _dbus_list_get_first_link (&f->functions);
2364   while (link != NULL)
2365     {
2366       Function *func = link->data;
2367
2368       if (func->n_nontest_blocks_executed <
2369           func->n_nontest_blocks)
2370         {
2371           funcs[i] = func;
2372           ++i;
2373         }
2374
2375       link = _dbus_list_get_next_link (&f->functions, link);
2376     }
2377
2378   _dbus_assert (i == n_found);
2379   
2380   qsort (funcs, n_found, sizeof (Function*),
2381          func_cmp);
2382   
2383   printf ("Incomplete functions in %s\n", f->name);
2384   printf ("=======\n");
2385
2386   i = 0;
2387   while (i < n_found)
2388     {
2389       Function *func = funcs[i];
2390
2391       printf ("  %s (%d/%d untested blocks)\n",
2392               func->name,
2393               func->n_nontest_blocks - func->n_nontest_blocks_executed,
2394               func->n_nontest_blocks);
2395       
2396       ++i;
2397     }
2398
2399   dbus_free (funcs);
2400
2401   printf ("\n");
2402 }
2403
2404 static void
2405 print_stats (Stats      *stats,
2406              const char *of_what)
2407 {
2408   int completely;
2409   
2410   printf ("Summary (%s)\n", of_what);
2411   printf ("=======\n");
2412   printf ("  %g%% blocks executed (%d of %d)\n",
2413           (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0,
2414           stats->n_blocks_executed,
2415           stats->n_blocks);
2416
2417   printf ("     (ignored %d blocks of test-only/debug-only code)\n",
2418           stats->n_blocks_inside_dbus_build_tests);
2419       
2420   printf ("  %g%% functions executed (%d of %d)\n",
2421           (stats->n_functions_executed / (double) stats->n_functions) * 100.0,
2422           stats->n_functions_executed,
2423           stats->n_functions);
2424
2425   completely = stats->n_functions_executed - stats->n_functions_partial;
2426   printf ("  %g%% functions completely executed (%d of %d)\n",
2427           (completely / (double) stats->n_functions) * 100.0,
2428           completely,
2429           stats->n_functions);
2430
2431   printf ("     (ignored %d functions of test-only/debug-only code)\n",
2432           stats->n_functions_inside_dbus_build_tests);
2433       
2434   printf ("  %g%% lines executed (%d of %d)\n",
2435           (stats->n_lines_executed / (double) stats->n_lines) * 100.0,
2436           stats->n_lines_executed,
2437           stats->n_lines);
2438
2439   completely = stats->n_lines_executed - stats->n_lines_partial;
2440   printf ("  %g%% lines completely executed (%d of %d)\n",
2441           (completely / (double) stats->n_lines) * 100.0,
2442           completely,
2443           stats->n_lines);
2444
2445   printf ("     (ignored %d lines of test-only/debug-only code)\n",
2446           stats->n_lines_inside_dbus_build_tests);
2447
2448   printf ("\n");
2449 }
2450
2451 typedef enum
2452 {
2453   MODE_PRINT,
2454   MODE_REPORT,
2455   MODE_BLOCKS,
2456   MODE_GCOV
2457 } Mode;
2458
2459 int
2460 main (int argc, char **argv)
2461 {
2462   DBusString filename;
2463   int i;
2464   Mode m;
2465   
2466   if (argc < 2)
2467     {
2468       fprintf (stderr, "Must specify files on command line\n");
2469       return 1;
2470     }
2471
2472   m = MODE_PRINT;
2473   i = 1;
2474
2475   if (strcmp (argv[i], "--report") == 0)
2476     {
2477       m = MODE_REPORT;
2478       ++i;
2479     }
2480   else if (strcmp (argv[i], "--blocks") == 0)
2481     {
2482       m = MODE_BLOCKS;
2483       ++i;
2484     }
2485   else if (strcmp (argv[i], "--gcov") == 0)
2486     {
2487       m = MODE_GCOV;
2488       ++i;
2489     }
2490
2491   
2492   if (i == argc)
2493     {
2494       fprintf (stderr, "Must specify files on command line\n");
2495       return 1;
2496     }
2497
2498   if (m == MODE_PRINT)
2499     {
2500       while (i < argc)
2501         {
2502           _dbus_string_init_const (&filename, argv[i]);
2503           
2504           print_one_file (&filename);
2505           
2506           ++i;
2507         }
2508     }
2509   else if (m == MODE_BLOCKS || m == MODE_GCOV)
2510     {
2511       while (i < argc)
2512         {
2513           File *f;
2514           
2515           _dbus_string_init_const (&filename, argv[i]);
2516       
2517           f = load_c_file (&filename);
2518
2519           if (m == MODE_BLOCKS)
2520             print_block_superdetails (f);
2521           else if (m == MODE_GCOV)
2522             print_annotated_source_gcov_format (f);
2523           
2524           ++i;
2525         }
2526     }
2527   else if (m == MODE_REPORT)
2528     {
2529       Stats stats = { 0, };
2530       DBusList *files;
2531       DBusList *link;
2532       DBusHashTable *stats_by_dir;
2533       DBusHashIter iter;
2534       
2535       files = NULL;
2536       while (i < argc)
2537         {
2538           _dbus_string_init_const (&filename, argv[i]);
2539
2540           if (_dbus_string_ends_with_c_str (&filename, ".c"))
2541             {
2542               File *f;
2543               
2544               f = load_c_file (&filename);
2545               
2546               if (!_dbus_list_append (&files, f))
2547                 die ("no memory\n");
2548             }
2549           else
2550             {
2551               fprintf (stderr, "Unknown file type %s\n",
2552                        _dbus_string_get_const_data (&filename));
2553               exit (1);
2554             }
2555           
2556           ++i;
2557         }
2558
2559       link = _dbus_list_get_first_link (&files);
2560       while (link != NULL)
2561         {
2562           File *f = link->data;
2563
2564           merge_stats_for_file (&stats, f);
2565           
2566           link = _dbus_list_get_next_link (&files, link);
2567         }
2568
2569       print_stats (&stats, "all files");
2570
2571       stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING,
2572                                            dbus_free, dbus_free);
2573       
2574       link = _dbus_list_get_first_link (&files);
2575       while (link != NULL)
2576         {
2577           File *f = link->data;
2578           DBusString dirname;
2579           char *dirname_c;
2580           Stats *dir_stats;
2581           
2582           _dbus_string_init_const (&filename, f->name);
2583             
2584           if (!_dbus_string_init (&dirname))
2585             die ("no memory\n");
2586
2587           if (!_dbus_string_get_dirname (&filename, &dirname) ||
2588               !_dbus_string_copy_data (&dirname, &dirname_c))
2589             die ("no memory\n");
2590
2591           dir_stats = _dbus_hash_table_lookup_string (stats_by_dir,
2592                                                       dirname_c);
2593
2594           if (dir_stats == NULL)
2595             {
2596               dir_stats = dbus_new0 (Stats, 1);
2597               if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c,
2598                                                    dir_stats))
2599                 die ("no memory\n");
2600             }
2601           else
2602             dbus_free (dirname_c);
2603           
2604           merge_stats_for_file (dir_stats, f);
2605           
2606           link = _dbus_list_get_next_link (&files, link);
2607         }
2608
2609       _dbus_hash_iter_init (stats_by_dir, &iter);
2610       while (_dbus_hash_iter_next (&iter))
2611         {
2612           const char *dirname = _dbus_hash_iter_get_string_key (&iter);
2613           Stats *dir_stats = _dbus_hash_iter_get_value (&iter);
2614
2615           print_stats (dir_stats, dirname);
2616         }
2617
2618       _dbus_hash_table_unref (stats_by_dir);
2619
2620       link = _dbus_list_get_first_link (&files);
2621       while (link != NULL)
2622         {
2623           File *f = link->data;
2624
2625           print_untested_functions (f);
2626           
2627           link = _dbus_list_get_next_link (&files, link);
2628         }
2629
2630       link = _dbus_list_get_first_link (&files);
2631       while (link != NULL)
2632         {
2633           File *f = link->data;
2634
2635           print_poorly_tested_functions (f, &stats);
2636           
2637           link = _dbus_list_get_next_link (&files, link);
2638         }
2639
2640       link = _dbus_list_get_first_link (&files);
2641       while (link != NULL)
2642         {
2643           File *f = link->data;
2644           
2645           print_n_untested_blocks_by_function (f, &stats);
2646           
2647           link = _dbus_list_get_next_link (&files, link);
2648         }
2649     }
2650   
2651   return 0;
2652 }