2003-04-05 Havoc Pennington <hp@pobox.com>
[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 #undef DBUS_COMPILATION
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 static void
41 die (const char *message)
42 {
43   fprintf (stderr, "%s", message);
44   exit (1);
45 }
46
47 /* This bizarro function is from gcov-io.h in gcc source tree */
48 static int
49 fetch_long (long        *dest,
50             const char  *source,
51             size_t       bytes)
52 {
53   long value = 0;
54   int i;
55                                                                                 
56   for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
57     if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
58       return 1;
59                                                                                 
60   for (; i >= 0; i--)
61     value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
62                                                                                 
63   if ((source[bytes - 1] & 128) && (value > 0))
64     value = - value;
65                                                                                 
66   *dest = value;
67   return 0;
68 }
69
70 typedef DBUS_INT64_TYPE dbus_int64_t;
71
72 static int
73 fetch_long64 (dbus_int64_t *dest,
74               const char   *source,
75               size_t        bytes)
76 {
77   dbus_int64_t value = 0;
78   int i;
79                                                                                 
80   for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
81     if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
82       return 1;
83                                                                                 
84   for (; i >= 0; i--)
85     value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
86                                                                                 
87   if ((source[bytes - 1] & 128) && (value > 0))
88     value = - value;
89                                                                                 
90   *dest = value;
91   return 0;
92 }
93
94 #define BB_FILENAME     (-1)
95 #define BB_FUNCTION     (-2)
96 #define BB_ENDOFLIST    0
97
98 static dbus_bool_t
99 string_get_int (const DBusString *str,
100                 int               start,
101                 long             *val)
102 {
103   const char *p;
104   
105   if ((_dbus_string_get_length (str) - start) < 4)
106     return FALSE;
107
108   p = _dbus_string_get_const_data (str);
109
110   p += start;
111
112   fetch_long (val, p, 4);
113   
114   return TRUE;
115 }
116
117 static dbus_bool_t
118 string_get_int64 (const DBusString *str,
119                   int               start,
120                   dbus_int64_t     *val)
121 {
122   const char *p;
123   
124   if ((_dbus_string_get_length (str) - start) < 8)
125     return FALSE;
126
127   p = _dbus_string_get_const_data (str);
128
129   p += start;
130
131   fetch_long64 (val, p, 8);
132   
133   return TRUE;
134 }
135
136 static dbus_bool_t
137 string_get_string (const DBusString *str,
138                    int               start,
139                    long              terminator,
140                    DBusString       *val,
141                    int              *end)
142 {
143   int i;
144   long n;
145   
146   i = start;
147   while (string_get_int (str, i, &n))
148     {
149       i += 4;
150       
151       if (n == terminator)
152         break;
153
154       _dbus_string_append_byte (val, n & 0xff);
155       _dbus_string_append_byte (val, (n >> 8) & 0xff);
156       _dbus_string_append_byte (val, (n >> 16) & 0xff);
157       _dbus_string_append_byte (val, (n >> 24) & 0xff);
158     }
159
160   *end = i;
161   
162   return TRUE;
163 }
164
165 static void
166 dump_bb_file (const DBusString *contents)
167 {
168   int i;
169   long val;
170   int n_functions;
171
172   n_functions = 0;
173   i = 0;
174   while (string_get_int (contents, i, &val))
175     {
176       i += 4;
177       
178       switch (val)
179         {
180         case BB_FILENAME:
181           {
182             DBusString f;
183
184             if (!_dbus_string_init (&f))
185               die ("no memory\n");
186
187             if (string_get_string (contents, i,
188                                    BB_FILENAME,
189                                    &f, &i))
190               {
191                 printf ("File %s\n", _dbus_string_get_const_data (&f));
192               }
193             _dbus_string_free (&f);
194           }
195           break;
196         case BB_FUNCTION:
197           {
198             DBusString f;
199             if (!_dbus_string_init (&f))
200               die ("no memory\n");
201
202             if (string_get_string (contents, i,
203                                    BB_FUNCTION,
204                                    &f, &i))
205               {
206                 printf ("Function %s\n", _dbus_string_get_const_data (&f));
207               }
208             _dbus_string_free (&f);
209
210             n_functions += 1;
211           }
212           break;
213         case BB_ENDOFLIST:
214           printf ("End of block\n");
215           break;
216         default:
217           printf ("Line %ld\n", val);
218           break;
219         }
220     }
221
222   printf ("%d functions in file\n", n_functions);
223 }
224
225 #define FLAG_ON_TREE 0x1
226 #define FLAG_FAKE 0x2
227 #define FLAG_FALL_THROUGH 0x4
228
229 static void
230 dump_bbg_file (const DBusString *contents)
231 {
232   int i;
233   long val;
234   int n_functions;
235   int n_arcs;
236   int n_blocks;
237   int n_arcs_off_tree;
238
239   n_arcs_off_tree = 0;
240   n_blocks = 0;
241   n_arcs = 0;
242   n_functions = 0;
243   i = 0;
244   while (string_get_int (contents, i, &val))
245     {
246       long n_blocks_in_func;
247       long n_arcs_in_func; 
248       int j;
249       
250       n_blocks_in_func = val;
251       
252       i += 4;
253
254       if (!string_get_int (contents, i, &n_arcs_in_func))
255         break;
256
257       i += 4;
258
259       printf ("Function has %ld blocks and %ld arcs\n",
260               n_blocks_in_func, n_arcs_in_func);
261
262       n_functions += 1;
263       n_blocks += n_blocks_in_func;
264       n_arcs += n_arcs_in_func;
265       
266       j = 0;
267       while (j < n_blocks_in_func)
268         {
269           long n_arcs_in_block;
270           int k;
271           
272           if (!string_get_int (contents, i, &n_arcs_in_block))
273             break;
274
275           i += 4;
276
277           printf ("  Block has %ld arcs\n", n_arcs_in_block);
278           
279           k = 0;
280           while (k < n_arcs_in_block)
281             {
282               long destination_block;
283               long flags;
284               
285               if (!string_get_int (contents, i, &destination_block))
286                 break;
287
288               i += 4;
289               
290               if (!string_get_int (contents, i, &flags))
291                 break;
292
293               i += 4;
294
295               printf ("    Arc has destination block %ld flags 0x%lx\n",
296                       destination_block, flags);
297
298               if ((flags & FLAG_ON_TREE) == 0)
299                 n_arcs_off_tree += 1;
300               
301               ++k;
302             }
303
304           if (k < n_arcs_in_block)
305             break;
306           
307           ++j;
308         }
309
310       if (j < n_blocks_in_func)
311         break;
312
313       if (!string_get_int (contents, i, &val))
314         break;
315
316       i += 4;
317
318       if (val != -1)
319         die ("-1 separator not found\n");
320     }
321
322   printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
323           n_functions, n_blocks, n_arcs, n_arcs_off_tree);
324 }
325
326 /* The da file contains first a count of arcs in the file,
327  * then a count of executions for all "off tree" arcs
328  * in the file.
329  */
330 static void
331 dump_da_file (const DBusString *contents)
332 {
333   int i;
334   dbus_int64_t val;
335   int n_arcs;
336   int claimed_n_arcs;
337
338   i = 0;
339   if (!string_get_int64 (contents, i, &val))
340     return;
341
342   i += 8;
343   
344   printf ("%ld arcs in file\n", (long) val);
345   claimed_n_arcs = val;
346   
347   n_arcs = 0;
348   while (string_get_int64 (contents, i, &val))
349     {
350       i += 8;
351
352       printf ("%ld executions of arc %d\n",
353               (long) val, n_arcs);
354
355       ++n_arcs;
356     }
357
358   if (n_arcs != claimed_n_arcs)
359     {
360       printf ("File claimed to have %d arcs but only had %d\n",
361               claimed_n_arcs, n_arcs);
362     }
363 }
364
365 typedef struct Arc Arc;
366 typedef struct Block Block;
367 typedef struct Function Function;
368 typedef struct File File;
369 typedef struct Line Line;
370
371 struct Arc
372 {
373   int source;
374   int target;
375   dbus_int64_t arc_count;
376   unsigned int count_valid : 1;
377   unsigned int on_tree : 1;
378   unsigned int fake : 1;
379   unsigned int fall_through : 1;
380   Arc *pred_next;
381   Arc *succ_next;
382 };
383
384 struct Block
385 {
386   Arc *succ;
387   Arc *pred;
388   dbus_int64_t succ_count;
389   dbus_int64_t pred_count;
390   dbus_int64_t exec_count;
391   DBusList *lines;
392   unsigned int count_valid : 1;
393   unsigned int on_tree : 1;
394   unsigned int inside_dbus_build_tests : 1;
395 };
396
397 struct Function
398 {
399   char *name;
400   Block *block_graph;
401   int n_blocks;
402   unsigned int unused : 1;
403   unsigned int inside_dbus_build_tests : 1;
404   unsigned int partial : 1; /* only some of the blocks were executed */
405 };
406
407 struct Line
408 {
409   int    number;
410   char  *text;
411   DBusList *blocks;
412   unsigned int inside_dbus_build_tests : 1;
413   unsigned int partial : 1; /* only some of the blocks were executed */
414 };
415
416 struct File
417 {
418   char *name;
419   Line *lines;
420   int   n_lines;
421   DBusList *functions;
422 };
423
424 static void
425 function_add_arc (Function *function,
426                   long      source,
427                   long      target,
428                   long      flags)
429 {
430   Arc *arc;
431
432   arc = dbus_new0 (Arc, 1);
433   if (arc == NULL)
434     die ("no memory\n");
435   
436   arc->target = target;
437   arc->source = source;
438
439   arc->succ_next = function->block_graph[source].succ;
440   function->block_graph[source].succ = arc;
441   function->block_graph[source].succ_count += 1;
442
443   arc->pred_next = function->block_graph[target].pred;
444   function->block_graph[target].pred = arc;
445   function->block_graph[target].pred_count += 1;
446
447   if ((flags & FLAG_ON_TREE) != 0)
448     arc->on_tree = TRUE;
449
450   if ((flags & FLAG_FAKE) != 0)
451     arc->fake = TRUE;
452
453   if ((flags & FLAG_FALL_THROUGH) != 0)
454     arc->fall_through = TRUE;
455 }
456
457
458 static Arc*
459 reverse_arcs (Arc *arc)
460 {
461   struct Arc *prev = 0;
462   struct Arc *next;
463
464   for ( ; arc; arc = next)
465     {
466       next = arc->succ_next;
467       arc->succ_next = prev;
468       prev = arc;
469     }
470
471   return prev;
472 }
473
474 static void
475 function_reverse_succ_arcs (Function *func)
476 {
477   /* Must reverse the order of all succ arcs, to ensure that they match
478    * the order of the data in the .da file.
479    */
480   int i;
481   
482   for (i = 0; i < func->n_blocks; i++)
483     if (func->block_graph[i].succ)
484       func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ);
485 }
486
487 static void
488 get_functions_from_bbg (const DBusString  *contents,
489                         DBusList         **functions)
490 {
491   int i;
492   long val;
493   int n_functions;
494   int n_arcs;
495   int n_blocks;
496   int n_arcs_off_tree;
497
498 #if 0
499   printf ("Loading arcs and blocks from .bbg file\n");
500 #endif
501   
502   n_arcs_off_tree = 0;
503   n_blocks = 0;
504   n_arcs = 0;
505   n_functions = 0;
506   i = 0;
507   while (string_get_int (contents, i, &val))
508     {
509       Function *func;
510       long n_blocks_in_func;
511       long n_arcs_in_func; 
512       int j;
513       
514       n_blocks_in_func = val;
515       
516       i += 4;
517
518       if (!string_get_int (contents, i, &n_arcs_in_func))
519         break;
520
521       i += 4;
522
523       n_functions += 1;
524       n_blocks += n_blocks_in_func;
525       n_arcs += n_arcs_in_func;
526
527       func = dbus_new0 (Function, 1);
528       if (func == NULL)
529         die ("no memory\n");
530
531       func->block_graph = dbus_new0 (Block, n_blocks_in_func);
532       func->n_blocks = n_blocks_in_func;
533       
534       j = 0;
535       while (j < n_blocks_in_func)
536         {
537           long n_arcs_in_block;
538           int k;
539           
540           if (!string_get_int (contents, i, &n_arcs_in_block))
541             break;
542
543           i += 4;
544           
545           k = 0;
546           while (k < n_arcs_in_block)
547             {
548               long destination_block;
549               long flags;
550               
551               if (!string_get_int (contents, i, &destination_block))
552                 break;
553
554               i += 4;
555               
556               if (!string_get_int (contents, i, &flags))
557                 break;
558
559               i += 4;
560
561               if ((flags & FLAG_ON_TREE) == 0)
562                 n_arcs_off_tree += 1;
563
564               function_add_arc (func, j, destination_block,
565                                 flags);
566               
567               ++k;
568             }
569
570           if (k < n_arcs_in_block)
571             break;
572           
573           ++j;
574         }
575
576       if (j < n_blocks_in_func)
577         break;
578
579       function_reverse_succ_arcs (func);
580       
581       if (!_dbus_list_append (functions, func))
582         die ("no memory\n");
583       
584       if (!string_get_int (contents, i, &val))
585         break;
586
587       i += 4;
588
589       if (val != -1)
590         die ("-1 separator not found\n");
591     }
592
593 #if 0
594   printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
595           n_functions, n_blocks, n_arcs, n_arcs_off_tree);
596 #endif
597   
598   _dbus_assert (n_functions == _dbus_list_get_length (functions));
599 }
600
601 static void
602 add_counts_from_da (const DBusString  *contents,
603                     DBusList         **functions)
604 {
605   int i;
606   dbus_int64_t val;
607   int n_arcs;
608   int claimed_n_arcs;
609   DBusList *link;
610   Function *current_func;  
611   int current_block;
612   Arc *current_arc;
613
614 #if 0
615   printf ("Loading execution count for each arc from .da file\n");
616 #endif
617   
618   i = 0;
619   if (!string_get_int64 (contents, i, &val))
620     return;
621
622   i += 8;
623   
624   claimed_n_arcs = val;
625
626   link = _dbus_list_get_first_link (functions);
627   if (link == NULL)
628     goto done;
629
630   current_func = link->data;
631   current_block = 0;
632   current_arc = current_func->block_graph[current_block].succ;
633   
634   n_arcs = 0;
635   while (string_get_int64 (contents, i, &val))
636     {
637       i += 8;
638
639       while (current_arc == NULL ||
640              current_arc->on_tree)
641         {
642           if (current_arc == NULL)
643             {
644               ++current_block;
645               
646               if (current_block == current_func->n_blocks)
647                 {
648                   link = _dbus_list_get_next_link (functions, link);
649                   if (link == NULL)
650                     {
651                       fprintf (stderr, "Ran out of functions loading .da file\n");
652                       goto done;
653                     }
654                   current_func = link->data;
655                   current_block = 0;
656                 }
657               
658               current_arc = current_func->block_graph[current_block].succ;
659             }
660           else
661             {
662               current_arc = current_arc->succ_next;
663             }
664         }
665
666       _dbus_assert (current_arc != NULL);
667       _dbus_assert (!current_arc->on_tree);
668
669       current_arc->arc_count = val;
670       current_arc->count_valid = TRUE;
671       current_func->block_graph[current_block].succ_count -= 1;
672       current_func->block_graph[current_arc->target].pred_count -= 1;
673       
674       ++n_arcs;
675
676       current_arc = current_arc->succ_next;
677     }
678
679  done:
680   
681   if (n_arcs != claimed_n_arcs)
682     {
683       fprintf (stderr, "File claimed to have %d arcs but only had %d\n",
684                claimed_n_arcs, n_arcs);
685       exit (1);
686     }
687
688 #if 0
689   printf ("%d arcs in file\n", n_arcs);
690 #endif
691 }
692
693 static void
694 function_solve_graph (Function *func)
695 {
696   int passes, changes;
697   dbus_int64_t total;
698   int i;
699   Arc *arc;
700   Block *block_graph;
701   int n_blocks;
702
703 #if 0
704   printf ("Solving function graph\n");
705 #endif
706   
707   n_blocks = func->n_blocks;
708   block_graph = func->block_graph;
709
710   /* For every block in the file,
711      - if every exit/entrance arc has a known count, then set the block count
712      - if the block count is known, and every exit/entrance arc but one has
713      a known execution count, then set the count of the remaining arc
714
715      As arc counts are set, decrement the succ/pred count, but don't delete
716      the arc, that way we can easily tell when all arcs are known, or only
717      one arc is unknown.  */
718
719   /* The order that the basic blocks are iterated through is important.
720      Since the code that finds spanning trees starts with block 0, low numbered
721      arcs are put on the spanning tree in preference to high numbered arcs.
722      Hence, most instrumented arcs are at the end.  Graph solving works much
723      faster if we propagate numbers from the end to the start.
724
725      This takes an average of slightly more than 3 passes.  */
726
727   changes = 1;
728   passes = 0;
729   while (changes)
730     {
731       passes++;
732       changes = 0;
733
734       for (i = n_blocks - 1; i >= 0; i--)
735         {
736           if (! block_graph[i].count_valid)
737             {
738               if (block_graph[i].succ_count == 0)
739                 {
740                   total = 0;
741                   for (arc = block_graph[i].succ; arc;
742                        arc = arc->succ_next)
743                     total += arc->arc_count;
744                   block_graph[i].exec_count = total;
745                   block_graph[i].count_valid = 1;
746                   changes = 1;
747                 }
748               else if (block_graph[i].pred_count == 0)
749                 {
750                   total = 0;
751                   for (arc = block_graph[i].pred; arc;
752                        arc = arc->pred_next)
753                     total += arc->arc_count;
754                   block_graph[i].exec_count = total;
755                   block_graph[i].count_valid = 1;
756                   changes = 1;
757                 }
758             }
759           if (block_graph[i].count_valid)
760             {
761               if (block_graph[i].succ_count == 1)
762                 {
763                   total = 0;
764                   /* One of the counts will be invalid, but it is zero,
765                      so adding it in also doesn't hurt.  */
766                   for (arc = block_graph[i].succ; arc;
767                        arc = arc->succ_next)
768                     total += arc->arc_count;
769                   /* Calculate count for remaining arc by conservation.  */
770                   total = block_graph[i].exec_count - total;
771                   /* Search for the invalid arc, and set its count.  */
772                   for (arc = block_graph[i].succ; arc;
773                        arc = arc->succ_next)
774                     if (! arc->count_valid)
775                       break;
776                   if (! arc)
777                     die ("arc == NULL\n");
778                   arc->count_valid = 1;
779                   arc->arc_count = total;
780                   block_graph[i].succ_count -= 1;
781
782                   block_graph[arc->target].pred_count -= 1;
783                   changes = 1;
784                 }
785               if (block_graph[i].pred_count == 1)
786                 {
787                   total = 0;
788                   /* One of the counts will be invalid, but it is zero,
789                      so adding it in also doesn't hurt.  */
790                   for (arc = block_graph[i].pred; arc;
791                        arc = arc->pred_next)
792                     total += arc->arc_count;
793                   /* Calculate count for remaining arc by conservation.  */
794                   total = block_graph[i].exec_count - total;
795                   /* Search for the invalid arc, and set its count.  */
796                   for (arc = block_graph[i].pred; arc;
797                        arc = arc->pred_next)
798                     if (! arc->count_valid)
799                       break;
800                   if (! arc)
801                     die ("arc == NULL\n");
802                   arc->count_valid = 1;
803                   arc->arc_count = total;
804                   block_graph[i].pred_count -= 1;
805
806                   block_graph[arc->source].succ_count -= 1;
807                   changes = 1;
808                 }
809             }
810         }
811     }
812
813   /* If the graph has been correctly solved, every block will have a
814    * succ and pred count of zero.
815    */
816   for (i = 0; i < n_blocks; i++)
817     {
818       if (block_graph[i].succ_count || block_graph[i].pred_count)
819         {
820           fprintf (stderr, "Block graph solved incorrectly\n");
821           fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n",
822                    i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count);
823           exit (1);
824         }
825     }
826 }
827
828 static void
829 solve_graphs (DBusList **functions)
830 {
831   DBusList *link;
832
833   link = _dbus_list_get_first_link (functions);
834   while (link != NULL)
835     {
836       Function *func = link->data;
837
838       function_solve_graph (func);
839       
840       link = _dbus_list_get_next_link (functions, link);
841     }
842 }
843
844 static void
845 load_functions_for_c_file (const DBusString *filename,
846                            DBusList        **functions)
847 {
848   DBusString bbg_filename;
849   DBusString da_filename;
850   DBusString contents;
851   DBusError error;
852
853   dbus_error_init (&error);
854   
855   if (!_dbus_string_init (&bbg_filename) ||
856       !_dbus_string_init (&da_filename) ||
857       !_dbus_string_copy (filename, 0, &bbg_filename, 0) ||
858       !_dbus_string_copy (filename, 0, &da_filename, 0) ||
859       !_dbus_string_init (&contents))
860     die ("no memory\n");
861
862   _dbus_string_shorten (&bbg_filename, 2);
863   _dbus_string_shorten (&da_filename, 2);
864
865   if (!_dbus_string_append (&bbg_filename, ".bbg") ||
866       !_dbus_string_append (&da_filename, ".da"))
867     die ("no memory\n");
868       
869   if (!_dbus_file_get_contents (&contents, &bbg_filename,
870                                 &error))
871     {
872       fprintf (stderr, "Could not open file: %s\n",
873                error.message);
874       exit (1);
875     }
876
877   get_functions_from_bbg (&contents, functions);
878
879   _dbus_string_set_length (&contents, 0);
880
881   if (!_dbus_file_get_contents (&contents, &da_filename,
882                                 &error))
883     {
884       fprintf (stderr, "Could not open file: %s\n",
885                error.message);
886       exit (1);
887     }
888   
889   add_counts_from_da (&contents, functions);
890   
891   solve_graphs (functions);
892
893   _dbus_string_free (&contents);
894   _dbus_string_free (&da_filename);
895   _dbus_string_free (&bbg_filename);
896 }
897
898 static void
899 get_lines_from_bb_file (const DBusString *contents,
900                         File             *fl)
901 {
902   int i;
903   long val;
904   int n_functions;
905   dbus_bool_t in_our_file;
906   DBusList *link;
907   Function *func;
908   int block;
909
910 #if 0
911   printf ("Getting line numbers for blocks from .bb file\n");
912 #endif
913   
914   /* There's this "filename" field in the .bb file which
915    * mysteriously comes *after* the first function in the
916    * file in the .bb file; and every .bb file seems to
917    * have only one filename. I don't understand
918    * what's going on here, so just set in_our_file = TRUE
919    * at the start categorically.
920    */
921   
922   block = 0;
923   func = NULL;
924   in_our_file = TRUE;
925   link = _dbus_list_get_first_link (&fl->functions);
926   n_functions = 0;
927   i = 0;
928   while (string_get_int (contents, i, &val))
929     {
930       i += 4;
931       
932       switch (val)
933         {
934         case BB_FILENAME:
935           {
936             DBusString f;
937
938             if (!_dbus_string_init (&f))
939               die ("no memory\n");
940
941             if (string_get_string (contents, i,
942                                    BB_FILENAME,
943                                    &f, &i))
944               {
945                 /* fl->name is a full path and the filename in .bb is
946                  * not.
947                  */
948                 DBusString tmp_str;
949
950                 _dbus_string_init_const (&tmp_str, fl->name);
951                 
952                 if (_dbus_string_ends_with_c_str (&tmp_str,
953                                                   _dbus_string_get_const_data (&f)))
954                   in_our_file = TRUE;
955                 else
956                   in_our_file = FALSE;
957                 
958 #if 0
959                 fprintf (stderr,
960                          "File %s in .bb, looking for %s, in_our_file = %d\n",
961                          _dbus_string_get_const_data (&f),
962                          fl->name,
963                          in_our_file);
964 #endif
965               }
966             _dbus_string_free (&f);
967           }
968           break;
969         case BB_FUNCTION:
970           {
971             DBusString f;
972             if (!_dbus_string_init (&f))
973               die ("no memory\n");
974
975             if (string_get_string (contents, i,
976                                    BB_FUNCTION,
977                                    &f, &i))
978               {
979 #if 0
980                 fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f));
981 #endif
982
983                 block = 0;
984                 
985                 if (in_our_file)
986                   {
987                     if (link == NULL)
988                       {
989                         fprintf (stderr, "No function object for function %s\n",
990                                  _dbus_string_get_const_data (&f));
991                       }
992                     else
993                       {
994                         func = link->data;
995                         link = _dbus_list_get_next_link (&fl->functions, link);
996                         
997                         if (func->name == NULL)
998                           {
999                             if (!_dbus_string_copy_data (&f, &func->name))
1000                               die ("no memory\n");
1001                           }
1002                         else
1003                           {
1004                             die ("got two names for function?\n");
1005                           }
1006                       }
1007                   }
1008               }
1009             _dbus_string_free (&f);
1010
1011             n_functions += 1;
1012           }
1013           break;
1014         case BB_ENDOFLIST:
1015           block += 1;
1016           break;
1017         default:
1018 #if 0
1019           fprintf (stderr, "Line %ld\n", val);
1020 #endif
1021
1022           if (val >= fl->n_lines)
1023             {
1024               fprintf (stderr, "Line %ld but file only has %d lines\n",
1025                        val, fl->n_lines);
1026             }
1027           else if (func != NULL)
1028             {
1029               val -= 1; /* To convert the 1-based line number to 0-based */
1030               _dbus_assert (val >= 0);
1031               
1032               if (block < func->n_blocks)
1033                 {
1034                   if (!_dbus_list_append (&func->block_graph[block].lines,
1035                                           &fl->lines[val]))
1036                     die ("no memory\n");
1037                   
1038                   
1039                   if (!_dbus_list_append (&fl->lines[val].blocks,
1040                                           &func->block_graph[block]))
1041                     die ("no memory\n");
1042                 }
1043               else
1044                 {
1045                   fprintf (stderr, "Line number for block %d but function only has %d blocks\n",
1046                            block, func->n_blocks);
1047                 }
1048             }
1049           else
1050             {
1051               fprintf (stderr, "Line %ld given outside of any function\n",
1052                        val);
1053             }
1054           
1055           break;
1056         }
1057     }
1058
1059 #if 0
1060   printf ("%d functions in file\n", n_functions);
1061 #endif
1062 }
1063
1064
1065 static void
1066 load_block_line_associations (const DBusString *filename,
1067                               File             *f)
1068 {
1069   DBusString bb_filename;
1070   DBusString contents;
1071   DBusError error;
1072
1073   dbus_error_init (&error);
1074   
1075   if (!_dbus_string_init (&bb_filename) ||
1076       !_dbus_string_copy (filename, 0, &bb_filename, 0) ||
1077       !_dbus_string_init (&contents))
1078     die ("no memory\n");
1079
1080   _dbus_string_shorten (&bb_filename, 2);
1081
1082   if (!_dbus_string_append (&bb_filename, ".bb"))
1083     die ("no memory\n");
1084       
1085   if (!_dbus_file_get_contents (&contents, &bb_filename,
1086                                 &error))
1087     {
1088       fprintf (stderr, "Could not open file: %s\n",
1089                error.message);
1090       exit (1);
1091     }
1092   
1093   get_lines_from_bb_file (&contents, f);
1094
1095   _dbus_string_free (&contents);
1096   _dbus_string_free (&bb_filename);
1097 }
1098
1099 static int
1100 count_lines_in_string (const DBusString *str)
1101 {
1102   int n_lines;
1103   const char *p;
1104   const char *prev;
1105   const char *end;
1106   const char *last_line_end;
1107
1108 #if 0
1109   printf ("Counting lines in source file\n");
1110 #endif
1111   
1112   n_lines = 0;  
1113   prev = NULL;
1114   p = _dbus_string_get_const_data (str);
1115   end = p + _dbus_string_get_length (str);
1116   last_line_end = p;
1117   while (p != end)
1118     {
1119       /* too lazy to handle \r\n as one linebreak */
1120       if (*p == '\n' || *p == '\r')
1121         {
1122           ++n_lines;
1123           last_line_end = p + 1;
1124         }
1125
1126       prev = p;
1127       ++p;
1128     }
1129
1130   if (last_line_end != p)
1131     ++n_lines;
1132   
1133   return n_lines;
1134 }
1135
1136 static void
1137 fill_line_content (const DBusString *str,
1138                    Line             *lines)
1139 {
1140   int n_lines;
1141   const char *p;
1142   const char *prev;
1143   const char *end;
1144   const char *last_line_end;
1145
1146 #if 0
1147   printf ("Saving contents of each line in source file\n");
1148 #endif
1149   
1150   n_lines = 0;
1151   prev = NULL;
1152   p = _dbus_string_get_const_data (str);
1153   end = p + _dbus_string_get_length (str);
1154   last_line_end = p;
1155   while (p != end)
1156     {
1157       if (*p == '\n' || *p == '\r')
1158         {
1159           lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1);
1160           if (lines[n_lines].text == NULL)
1161             die ("no memory\n");
1162
1163           memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1164           lines[n_lines].number = n_lines + 1;
1165           
1166           ++n_lines;
1167
1168           last_line_end = p + 1;
1169         }
1170
1171       prev = p;
1172       ++p;
1173     }
1174
1175   if (p != last_line_end)
1176     {
1177       memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1178       ++n_lines;
1179     }
1180 }
1181
1182 static void
1183 mark_unused_functions (File  *f)
1184 {
1185   int i;
1186   DBusList *link;
1187
1188   link = _dbus_list_get_first_link (&f->functions);
1189   while (link != NULL)
1190     {
1191       Function *func = link->data;
1192       dbus_bool_t used;
1193
1194       used = FALSE;
1195       i = 0;
1196       while (i < func->n_blocks)
1197         {
1198           if (func->block_graph[i].exec_count > 0)
1199             used = TRUE;
1200           
1201           ++i;
1202         }
1203
1204       if (!used)
1205         func->unused = TRUE;
1206
1207       link = _dbus_list_get_next_link (&f->functions, link);
1208     }
1209 }
1210
1211 static void
1212 mark_inside_dbus_build_tests (File  *f)
1213 {
1214   int i;
1215   DBusList *link;
1216   int inside_depth;
1217
1218   inside_depth = 0;
1219   i = 0;
1220   while (i < f->n_lines)
1221     {
1222       Line *l = &f->lines[i];
1223
1224       if (inside_depth == 0)
1225         {
1226           const char *a, *b;
1227           
1228           a = strstr (l->text, "#ifdef");
1229           b = strstr (l->text, "DBUS_BUILD_TESTS");
1230           if (a && b)
1231             inside_depth += 1;
1232         }
1233       else
1234         {
1235           if (strstr (l->text, "#if") != NULL)
1236             inside_depth += 1;
1237           else if (strstr (l->text, "#endif") != NULL)
1238             inside_depth -= 1;
1239         }
1240
1241       if (inside_depth > 0)
1242         {
1243           /* Mark the line and its blocks */
1244           DBusList *blink;
1245
1246           l->inside_dbus_build_tests = TRUE;
1247           
1248           blink = _dbus_list_get_first_link (&l->blocks);
1249           while (blink != NULL)
1250             {
1251               Block *b = blink->data;
1252
1253               b->inside_dbus_build_tests = TRUE;
1254               
1255               blink = _dbus_list_get_next_link (&l->blocks, blink);
1256             }
1257         }
1258       
1259       ++i;
1260     }
1261
1262   /* Now mark functions where for all blocks that are associated
1263    * with a source line, the block is inside_dbus_build_tests.
1264    */
1265   link = _dbus_list_get_first_link (&f->functions);
1266   while (link != NULL)
1267     {
1268       Function *func = link->data;
1269       
1270       i = 0;
1271       while (i < func->n_blocks)
1272         {
1273           /* Break as soon as any block is not a test block */
1274           if (func->block_graph[i].lines != NULL &&
1275               !func->block_graph[i].inside_dbus_build_tests)
1276             break;
1277           
1278           ++i;
1279         }
1280
1281       if (i == func->n_blocks)
1282         func->inside_dbus_build_tests = TRUE;
1283       
1284       link = _dbus_list_get_next_link (&f->functions, link);
1285     } 
1286 }
1287
1288 static void
1289 mark_partials (File  *f)
1290 {
1291   int i;
1292   DBusList *link;
1293   
1294   i = 0;
1295   while (i < f->n_lines)
1296     {
1297       Line *l = &f->lines[i];
1298       DBusList *blink;
1299       int n_blocks;
1300       int n_blocks_executed;
1301
1302       n_blocks = 0;
1303       n_blocks_executed = 0;
1304       blink = _dbus_list_get_first_link (&l->blocks);
1305       while (blink != NULL)
1306         {
1307           Block *b = blink->data;
1308           
1309           if (b->exec_count > 0)
1310             n_blocks_executed += 1;
1311
1312           n_blocks += 1;
1313           
1314           blink = _dbus_list_get_next_link (&l->blocks, blink);
1315         }
1316
1317       if (n_blocks_executed > 0 &&
1318           n_blocks_executed < n_blocks)
1319         l->partial = TRUE;
1320
1321       ++i;
1322     }
1323
1324   link = _dbus_list_get_first_link (&f->functions);
1325   while (link != NULL)
1326     {
1327       Function *func = link->data;
1328       int i;
1329       int n_blocks;
1330       int n_blocks_executed;
1331
1332       n_blocks = 0;
1333       n_blocks_executed = 0;
1334       i = 0;
1335       while (i < func->n_blocks)
1336         {
1337           /* Break as soon as any block is not a test block */
1338           if (func->block_graph[i].exec_count > 0)
1339             n_blocks_executed += 1;
1340
1341           n_blocks += 1;
1342           ++i;
1343         }
1344
1345       if (n_blocks_executed > 0 &&
1346           n_blocks_executed < n_blocks)
1347         func->partial = TRUE;
1348       
1349       link = _dbus_list_get_next_link (&f->functions, link);
1350     }
1351 }
1352
1353 static File*
1354 load_c_file (const DBusString *filename)
1355 {
1356   DBusString contents;
1357   DBusError error;
1358   File *f;
1359   
1360   f = dbus_new0 (File, 1);
1361   if (f == NULL)
1362     die ("no memory\n");
1363
1364   if (!_dbus_string_copy_data (filename, &f->name))
1365     die ("no memory\n");
1366   
1367   if (!_dbus_string_init (&contents))
1368     die ("no memory\n");
1369       
1370   dbus_error_init (&error);
1371
1372   if (!_dbus_file_get_contents (&contents, filename,
1373                                 &error))
1374     {
1375       fprintf (stderr, "Could not open file: %s\n",
1376                error.message);
1377       dbus_error_free (&error);
1378       exit (1);
1379     }
1380       
1381   load_functions_for_c_file (filename, &f->functions);
1382
1383   f->n_lines = count_lines_in_string (&contents);
1384   f->lines = dbus_new0 (Line, f->n_lines);
1385   if (f->lines == NULL)
1386     die ("no memory\n");
1387
1388   fill_line_content (&contents, f->lines);
1389   
1390   _dbus_string_free (&contents);
1391
1392   load_block_line_associations (filename, f);
1393
1394   mark_unused_functions (f);
1395   mark_inside_dbus_build_tests (f);
1396   mark_partials (f);
1397   
1398   return f;
1399 }
1400
1401 typedef struct Stats Stats;
1402
1403 struct Stats
1404 {
1405   int n_blocks;
1406   int n_blocks_executed;
1407   int n_blocks_inside_dbus_build_tests;
1408   
1409   int n_lines; /* lines that have blocks on them */
1410   int n_lines_executed;
1411   int n_lines_partial;
1412   int n_lines_inside_dbus_build_tests;
1413   
1414   int n_functions;
1415   int n_functions_executed;
1416   int n_functions_partial;
1417   int n_functions_inside_dbus_build_tests;
1418 };
1419
1420 static dbus_bool_t
1421 line_was_executed (Line *l)
1422 {
1423   DBusList *link;
1424
1425   link = _dbus_list_get_first_link (&l->blocks);
1426   while (link != NULL)
1427     {
1428       Block *b = link->data;
1429
1430       if (b->exec_count > 0)
1431         return TRUE;
1432       
1433       link = _dbus_list_get_next_link (&l->blocks, link);
1434     }
1435
1436   return FALSE;
1437 }
1438
1439
1440 static int
1441 line_exec_count (Line *l)
1442 {
1443   DBusList *link;
1444   dbus_int64_t total;
1445
1446   total = 0;
1447   link = _dbus_list_get_first_link (&l->blocks);
1448   while (link != NULL)
1449     {
1450       Block *b = link->data;
1451
1452       total += b->exec_count;
1453       
1454       link = _dbus_list_get_next_link (&l->blocks, link);
1455     }
1456
1457   return total;
1458 }
1459
1460 static void
1461 merge_stats_for_file (Stats *stats,
1462                       File  *f)
1463 {
1464   int i;
1465   DBusList *link;
1466   
1467   for (i = 0; i < f->n_lines; ++i)
1468     {
1469       Line *l = &f->lines[i];
1470       
1471       if (l->inside_dbus_build_tests)
1472         {
1473           stats->n_lines_inside_dbus_build_tests += 1;
1474           continue;
1475         }
1476       
1477       if (line_was_executed (l))
1478         stats->n_lines_executed += 1;
1479
1480       if (l->blocks != NULL)
1481         stats->n_lines += 1;
1482
1483       if (l->partial)
1484         stats->n_lines_partial += 1;
1485     }
1486
1487   link = _dbus_list_get_first_link (&f->functions);
1488   while (link != NULL)
1489     {
1490       Function *func = link->data;
1491
1492       if (func->inside_dbus_build_tests)
1493         stats->n_functions_inside_dbus_build_tests += 1;
1494       else
1495         {
1496           stats->n_functions += 1;
1497
1498           if (!func->unused)
1499             stats->n_functions_executed += 1;
1500           
1501           if (func->partial)
1502             stats->n_functions_partial += 1;
1503         }
1504           
1505       i = 0;
1506       while (i < func->n_blocks)
1507         {
1508           Block *b = &func->block_graph[i];
1509
1510           if (b->inside_dbus_build_tests)
1511             stats->n_blocks_inside_dbus_build_tests += 1;
1512           else
1513             {
1514               if (b->exec_count > 0)
1515                 stats->n_blocks_executed += 1;
1516               
1517               stats->n_blocks += 1;
1518             }
1519           
1520           ++i;
1521         }
1522
1523       link = _dbus_list_get_next_link (&f->functions, link);
1524     }
1525 }
1526
1527 /* The output of this matches gcov exactly ("diff" shows no difference) */
1528 static void
1529 print_annotated_source_gcov_format (File *f)
1530 {
1531   int i;
1532   
1533   i = 0;
1534   while (i < f->n_lines)
1535     {
1536       Line *l = &f->lines[i];
1537
1538       if (l->blocks != NULL)
1539         {
1540           int exec_count;
1541           
1542           exec_count = line_exec_count (l);
1543           
1544           if (exec_count > 0)
1545             printf ("%12d    %s\n",
1546                     exec_count, l->text);
1547           else
1548             printf ("      ######    %s\n", l->text);
1549         }
1550       else
1551         {
1552           printf ("\t\t%s\n", l->text);
1553         }
1554           
1555       ++i;
1556     }
1557 }
1558
1559 static void
1560 print_annotated_source (File *f)
1561 {
1562   int i;
1563   
1564   i = 0;
1565   while (i < f->n_lines)
1566     {
1567       Line *l = &f->lines[i];
1568
1569       if (l->inside_dbus_build_tests)
1570         printf ("*");
1571       else
1572         printf (" ");
1573       
1574       if (l->blocks != NULL)
1575         {
1576           int exec_count;
1577           
1578           exec_count = line_exec_count (l);
1579           
1580           if (exec_count > 0)
1581             printf ("%12d    %s\n",
1582                     exec_count, l->text);
1583           else
1584             printf ("      ######    %s\n", l->text);
1585         }
1586       else
1587         {
1588           printf ("\t\t%s\n", l->text);
1589         }
1590           
1591       ++i;
1592     }
1593 }
1594
1595 static void
1596 print_block_superdetails (File *f)
1597 {
1598   DBusList *link;
1599   int i;
1600   
1601   link = _dbus_list_get_first_link (&f->functions);
1602   while (link != NULL)
1603     {
1604       Function *func = link->data;
1605
1606       printf ("=== %s():\n", func->name);
1607
1608       i = 0;
1609       while (i < func->n_blocks)
1610         {
1611           Block *b = &func->block_graph[i];
1612           DBusList *l;
1613           
1614           printf ("  %5d executed %d times%s\n", i,
1615                   (int) b->exec_count,
1616                   b->inside_dbus_build_tests ?
1617                   " [inside DBUS_BUILD_TESTS]" : "");
1618                   
1619           l = _dbus_list_get_first_link (&b->lines);
1620           while (l != NULL)
1621             {
1622               Line *line = l->data;
1623
1624               printf ("4%d\t%s\n", line->number, line->text);
1625
1626               l = _dbus_list_get_next_link (&b->lines, l);
1627             }
1628           
1629           ++i;
1630         }
1631       
1632       link = _dbus_list_get_next_link (&f->functions, link);
1633     }
1634 }
1635
1636 static void
1637 print_one_file (const DBusString *filename)
1638 {
1639   if (_dbus_string_ends_with_c_str (filename, ".bb"))
1640     {
1641       DBusString contents;
1642       DBusError error;
1643       
1644       if (!_dbus_string_init (&contents))
1645         die ("no memory\n");
1646       
1647       dbus_error_init (&error);
1648
1649       if (!_dbus_file_get_contents (&contents, filename,
1650                                     &error))
1651         {
1652           fprintf (stderr, "Could not open file: %s\n",
1653                    error.message);
1654           dbus_error_free (&error);
1655           exit (1);
1656         }
1657       
1658       dump_bb_file (&contents);
1659
1660       _dbus_string_free (&contents);
1661     }
1662   else if (_dbus_string_ends_with_c_str (filename, ".bbg"))
1663     {
1664       DBusString contents;
1665       DBusError error;
1666       
1667       if (!_dbus_string_init (&contents))
1668         die ("no memory\n");
1669       
1670       dbus_error_init (&error);
1671
1672       if (!_dbus_file_get_contents (&contents, filename,
1673                                     &error))
1674         {
1675           fprintf (stderr, "Could not open file: %s\n",
1676                    error.message);
1677           dbus_error_free (&error);
1678           exit (1);
1679         }
1680       
1681       dump_bbg_file (&contents);
1682
1683       _dbus_string_free (&contents);
1684     }
1685   else if (_dbus_string_ends_with_c_str (filename, ".da"))
1686     {
1687       DBusString contents;
1688       DBusError error;
1689       
1690       if (!_dbus_string_init (&contents))
1691         die ("no memory\n");
1692       
1693       dbus_error_init (&error);
1694
1695       if (!_dbus_file_get_contents (&contents, filename,
1696                                     &error))
1697         {
1698           fprintf (stderr, "Could not open file: %s\n",
1699                    error.message);
1700           dbus_error_free (&error);
1701           exit (1);
1702         }
1703       
1704       dump_da_file (&contents);
1705
1706       _dbus_string_free (&contents);
1707     }
1708   else if (_dbus_string_ends_with_c_str (filename, ".c"))
1709     {
1710       File *f;
1711       
1712       f = load_c_file (filename);
1713
1714       print_annotated_source (f);
1715     }
1716   else
1717     {
1718       fprintf (stderr, "Unknown file type %s\n",
1719                _dbus_string_get_const_data (filename));
1720       exit (1);
1721     }
1722 }
1723
1724 static void
1725 print_untested_functions (File *f)
1726 {
1727   DBusList *link;
1728   dbus_bool_t found;
1729
1730   found = FALSE;
1731   link = _dbus_list_get_first_link (&f->functions);
1732   while (link != NULL)
1733     {
1734       Function *func = link->data;
1735
1736       if (func->unused &&
1737           !func->inside_dbus_build_tests)
1738         found = TRUE;
1739       
1740       link = _dbus_list_get_next_link (&f->functions, link);
1741     }
1742
1743   if (!found)
1744     return;
1745
1746   printf ("Untested functions in %s\n", f->name);
1747   printf ("=======\n");
1748   
1749   link = _dbus_list_get_first_link (&f->functions);
1750   while (link != NULL)
1751     {
1752       Function *func = link->data;
1753
1754       if (func->unused &&
1755           !func->inside_dbus_build_tests)
1756         printf ("  %s\n", func->name);
1757       
1758       link = _dbus_list_get_next_link (&f->functions, link);
1759     }
1760
1761   printf ("\n");
1762 }
1763
1764 typedef enum
1765 {
1766   MODE_PRINT,
1767   MODE_REPORT,
1768   MODE_BLOCKS,
1769   MODE_GCOV
1770 } Mode;
1771
1772 int
1773 main (int argc, char **argv)
1774 {
1775   DBusString filename;
1776   int i;
1777   Mode m;
1778   
1779   if (argc < 2)
1780     {
1781       fprintf (stderr, "Must specify files on command line\n");
1782       return 1;
1783     }
1784
1785   m = MODE_PRINT;
1786   i = 1;
1787
1788   if (strcmp (argv[i], "--report") == 0)
1789     {
1790       m = MODE_REPORT;
1791       ++i;
1792     }
1793   else if (strcmp (argv[i], "--blocks") == 0)
1794     {
1795       m = MODE_BLOCKS;
1796       ++i;
1797     }
1798   else if (strcmp (argv[i], "--gcov") == 0)
1799     {
1800       m = MODE_GCOV;
1801       ++i;
1802     }
1803
1804   
1805   if (i == argc)
1806     {
1807       fprintf (stderr, "Must specify files on command line\n");
1808       return 1;
1809     }
1810
1811   if (m == MODE_PRINT)
1812     {
1813       while (i < argc)
1814         {
1815           _dbus_string_init_const (&filename, argv[i]);
1816           
1817           print_one_file (&filename);
1818           
1819           ++i;
1820         }
1821     }
1822   else if (m == MODE_BLOCKS || m == MODE_GCOV)
1823     {
1824       while (i < argc)
1825         {
1826           File *f;
1827           
1828           _dbus_string_init_const (&filename, argv[i]);
1829       
1830           f = load_c_file (&filename);
1831
1832           if (m == MODE_BLOCKS)
1833             print_block_superdetails (f);
1834           else if (m == MODE_GCOV)
1835             print_annotated_source_gcov_format (f);
1836           
1837           ++i;
1838         }
1839     }
1840   else if (m == MODE_REPORT)
1841     {
1842       Stats stats = { 0, };
1843       DBusList *files;
1844       DBusList *link;
1845       int completely;
1846       
1847       files = NULL;
1848       while (i < argc)
1849         {
1850           _dbus_string_init_const (&filename, argv[i]);
1851
1852           if (_dbus_string_ends_with_c_str (&filename, ".c"))
1853             {
1854               File *f;
1855               
1856               f = load_c_file (&filename);
1857               
1858               if (!_dbus_list_append (&files, f))
1859                 die ("no memory\n");
1860             }
1861           else
1862             {
1863               fprintf (stderr, "Unknown file type %s\n",
1864                        _dbus_string_get_const_data (&filename));
1865               exit (1);
1866             }
1867           
1868           ++i;
1869         }
1870
1871       link = _dbus_list_get_first_link (&files);
1872       while (link != NULL)
1873         {
1874           File *f = link->data;
1875
1876           merge_stats_for_file (&stats, f);
1877           
1878           link = _dbus_list_get_next_link (&files, link);
1879         }
1880
1881       printf ("Summary\n");
1882       printf ("=======\n");
1883       printf ("  %g%% blocks executed (%d of %d)\n",
1884               (stats.n_blocks_executed / (double) stats.n_blocks) * 100.0,
1885               stats.n_blocks_executed,
1886               stats.n_blocks);
1887
1888       printf ("     (ignored %d blocks inside DBUS_BUILD_TESTS)\n",
1889               stats.n_blocks_inside_dbus_build_tests);
1890       
1891       printf ("  %g%% functions executed (%d of %d)\n",
1892               (stats.n_functions_executed / (double) stats.n_functions) * 100.0,
1893               stats.n_functions_executed,
1894               stats.n_functions);
1895
1896       completely = stats.n_functions_executed - stats.n_functions_partial;
1897       printf ("  %g%% functions completely executed (%d of %d)\n",
1898               (completely / (double) stats.n_functions) * 100.0,
1899               completely,
1900               stats.n_functions);
1901
1902       printf ("     (ignored %d functions inside DBUS_BUILD_TESTS)\n",
1903               stats.n_functions_inside_dbus_build_tests);
1904       
1905       printf ("  %g%% lines executed (%d of %d)\n",
1906               (stats.n_lines_executed / (double) stats.n_lines) * 100.0,
1907               stats.n_lines_executed,
1908               stats.n_lines);
1909
1910       completely = stats.n_lines_executed - stats.n_lines_partial;
1911       printf ("  %g%% lines completely executed (%d of %d)\n",
1912               (completely / (double) stats.n_lines) * 100.0,
1913               completely,
1914               stats.n_lines);
1915
1916       printf ("     (ignored %d lines inside DBUS_BUILD_TESTS)\n",
1917               stats.n_lines_inside_dbus_build_tests);
1918
1919       printf ("\n");
1920       
1921       link = _dbus_list_get_first_link (&files);
1922       while (link != NULL)
1923         {
1924           File *f = link->data;
1925
1926           print_untested_functions (f);
1927           
1928           link = _dbus_list_get_next_link (&files, link);
1929         }      
1930     }
1931   
1932   return 0;
1933 }