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