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